mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Re-land patches from bug 566746 and bug 697377 now that bug 851641 is addressed
This commit is contained in:
parent
181686bf1e
commit
6ff5e33807
@ -447,6 +447,7 @@
|
||||
@BINPATH@/components/satchel.manifest
|
||||
@BINPATH@/components/nsFormAutoComplete.js
|
||||
@BINPATH@/components/nsFormHistory.js
|
||||
@BINPATH@/components/FormHistoryStartup.js
|
||||
@BINPATH@/components/nsInputListAutoComplete.js
|
||||
@BINPATH@/components/contentSecurityPolicy.manifest
|
||||
@BINPATH@/components/contentSecurityPolicy.js
|
||||
|
@ -6,6 +6,8 @@
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function Sanitizer() {}
|
||||
Sanitizer.prototype = {
|
||||
@ -16,9 +18,16 @@ Sanitizer.prototype = {
|
||||
this.items[aItemName].clear();
|
||||
},
|
||||
|
||||
canClearItem: function (aItemName)
|
||||
canClearItem: function (aItemName, aCallback, aArg)
|
||||
{
|
||||
return this.items[aItemName].canClear;
|
||||
let canClear = this.items[aItemName].canClear;
|
||||
if (typeof canClear == "function") {
|
||||
canClear(aCallback, aArg);
|
||||
return false;
|
||||
}
|
||||
|
||||
aCallback(aItemName, canClear, aArg);
|
||||
return canClear;
|
||||
},
|
||||
|
||||
prefDomain: "",
|
||||
@ -29,12 +38,10 @@ Sanitizer.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes privacy sensitive data in a batch, according to user preferences
|
||||
*
|
||||
* @returns null if everything's fine; an object in the form
|
||||
* { itemName: error, ... } on (partial) failure
|
||||
* Deletes privacy sensitive data in a batch, according to user preferences. Calls
|
||||
* errorHandler with a list of errors on failure.
|
||||
*/
|
||||
sanitize: function ()
|
||||
sanitize: function (errorHandler)
|
||||
{
|
||||
var psvc = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefService);
|
||||
@ -46,27 +53,42 @@ Sanitizer.prototype = {
|
||||
var range = null; // If we ignore timespan, clear everything
|
||||
else
|
||||
range = this.range || Sanitizer.getClearRange();
|
||||
|
||||
|
||||
let itemCount = this.items.length;
|
||||
for (var itemName in this.items) {
|
||||
var item = this.items[itemName];
|
||||
let item = this.items[itemName];
|
||||
item.range = range;
|
||||
if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) {
|
||||
// Some of these clear() may raise exceptions (see bug #265028)
|
||||
// to sanitize as much as possible, we catch and store them,
|
||||
// rather than fail fast.
|
||||
// Callers should check returned errors and give user feedback
|
||||
// about items that could not be sanitized
|
||||
try {
|
||||
item.clear();
|
||||
} catch(er) {
|
||||
if (!errors)
|
||||
errors = {};
|
||||
errors[itemName] = er;
|
||||
dump("Error sanitizing " + itemName + ": " + er + "\n");
|
||||
}
|
||||
if ("clear" in item && branch.getBoolPref(itemName)) {
|
||||
let clearCallback = (itemName, aCanClear) => {
|
||||
// Some of these clear() may raise exceptions (see bug #265028)
|
||||
// to sanitize as much as possible, we catch and store them,
|
||||
// rather than fail fast.
|
||||
// Callers should check returned errors and give user feedback
|
||||
// about items that could not be sanitized
|
||||
let item = this.items[itemName];
|
||||
try {
|
||||
if (aCanClear)
|
||||
item.clear();
|
||||
} catch(er) {
|
||||
if (!errors)
|
||||
errors = {};
|
||||
errors[itemName] = er;
|
||||
dump("Error sanitizing " + itemName + ": " + er + "\n");
|
||||
}
|
||||
|
||||
// If this is the last item that needs to receive the callback, call the error handler
|
||||
if (!--itemCount && errors) {
|
||||
errorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
this.canClearItem(itemName, clearCallback);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
|
||||
if (errors) {
|
||||
errorHandler(error);
|
||||
}
|
||||
},
|
||||
|
||||
// Time span only makes sense in certain cases. Consumers who want
|
||||
@ -231,15 +253,14 @@ Sanitizer.prototype = {
|
||||
findBar.clear();
|
||||
}
|
||||
|
||||
let formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
|
||||
.getService(Components.interfaces.nsIFormHistory2);
|
||||
if (this.range)
|
||||
formHistory.removeEntriesByTimeframe(this.range[0], this.range[1]);
|
||||
else
|
||||
formHistory.removeAllEntries();
|
||||
let change = { op: "remove" };
|
||||
if (this.range) {
|
||||
[ change.firstUsedStart, change.firstUsedEnd ] = this.range;
|
||||
}
|
||||
FormHistory.update(change);
|
||||
},
|
||||
|
||||
get canClear()
|
||||
canClear : function(aCallback, aArg)
|
||||
{
|
||||
var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
@ -251,17 +272,27 @@ Sanitizer.prototype = {
|
||||
let transactionMgr = searchBar.textbox.editor.transactionManager;
|
||||
if (searchBar.value ||
|
||||
transactionMgr.numberOfUndoItems ||
|
||||
transactionMgr.numberOfRedoItems)
|
||||
return true;
|
||||
transactionMgr.numberOfRedoItems) {
|
||||
aCallback("formdata", true, aArg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let findBar = currentDocument.getElementById("FindToolbar");
|
||||
if (findBar && findBar.canClear)
|
||||
return true;
|
||||
if (findBar && findBar.canClear) {
|
||||
aCallback("formdata", true, aArg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
|
||||
.getService(Components.interfaces.nsIFormHistory2);
|
||||
return formHistory.hasEntries;
|
||||
let count = 0;
|
||||
let countDone = {
|
||||
handleResult : function(aResult) count = aResult,
|
||||
handleError : function(aError) Components.utils.reportError(aError),
|
||||
handleCompletion :
|
||||
function(aReason) { aCallback("formdata", aReason == 0 && count > 0, aArg); }
|
||||
};
|
||||
FormHistory.count({}, countDone);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
@ -486,9 +517,7 @@ Sanitizer._checkAndSanitize = function()
|
||||
// this is a shutdown or a startup after an unclean exit
|
||||
var s = new Sanitizer();
|
||||
s.prefDomain = "privacy.clearOnShutdown.";
|
||||
s.sanitize() || // sanitize() returns null on full success
|
||||
prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
|
||||
let errorHandler = function() prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
|
||||
s.sanitize(errorHandler);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -47,11 +47,13 @@ var gSanitizePromptDialog = {
|
||||
for (let i = 0; i < sanitizeItemList.length; i++) {
|
||||
let prefItem = sanitizeItemList[i];
|
||||
let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
|
||||
if (!s.canClearItem(name)) {
|
||||
prefItem.preference = null;
|
||||
prefItem.checked = false;
|
||||
prefItem.disabled = true;
|
||||
}
|
||||
s.canClearItem(name, function canClearCallback(aItem, aCanClear, aPrefItem) {
|
||||
if (!aCanClear) {
|
||||
aPrefItem.preference = null;
|
||||
aPrefItem.checked = false;
|
||||
aPrefItem.disabled = true;
|
||||
}
|
||||
}, prefItem);
|
||||
}
|
||||
|
||||
document.documentElement.getButton("accept").label =
|
||||
@ -281,11 +283,13 @@ var gSanitizePromptDialog = {
|
||||
for (let i = 0; i < sanitizeItemList.length; i++) {
|
||||
let prefItem = sanitizeItemList[i];
|
||||
let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
|
||||
if (!s.canClearItem(name)) {
|
||||
prefItem.preference = null;
|
||||
prefItem.checked = false;
|
||||
prefItem.disabled = true;
|
||||
}
|
||||
s.canClearItem(name, function canClearCallback(aCanClear) {
|
||||
if (!aCanClear) {
|
||||
prefItem.preference = null;
|
||||
prefItem.checked = false;
|
||||
prefItem.disabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.documentElement.getButton("accept").label =
|
||||
|
@ -2,9 +2,24 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
// This test relies on the form history being empty to start with delete
|
||||
// all the items first.
|
||||
FormHistory.update({ op: "remove" },
|
||||
{ handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) test2(); },
|
||||
});
|
||||
}
|
||||
|
||||
function test2()
|
||||
{
|
||||
let prefService = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch2);
|
||||
|
||||
@ -29,13 +44,28 @@ function test() {
|
||||
prefBranch.setBoolPref("sessions", false);
|
||||
prefBranch.setBoolPref("siteSettings", false);
|
||||
|
||||
// Sanitize now so we can test that canClear is correct
|
||||
// Sanitize now so we can test that canClear is correct. Formdata is cleared asynchronously.
|
||||
s.sanitize();
|
||||
ok(!s.canClearItem("formdata"), "pre-test baseline for sanitizer");
|
||||
textbox.value = "m";
|
||||
ok(s.canClearItem("formdata"), "formdata can be cleared after input");
|
||||
s.sanitize();
|
||||
is(textbox.value, "", "findBar textbox should be empty after sanitize");
|
||||
ok(!s.canClearItem("formdata"), "canClear now false after sanitize");
|
||||
s.canClearItem("formdata", clearDone1, s);
|
||||
}
|
||||
|
||||
function clearDone1(aItemName, aResult, aSanitizer)
|
||||
{
|
||||
ok(!aResult, "pre-test baseline for sanitizer");
|
||||
gFindBar.getElement("findbar-textbox").value = "m";
|
||||
aSanitizer.canClearItem("formdata", inputEntered, aSanitizer);
|
||||
}
|
||||
|
||||
function inputEntered(aItemName, aResult, aSanitizer)
|
||||
{
|
||||
ok(aResult, "formdata can be cleared after input");
|
||||
aSanitizer.sanitize();
|
||||
aSanitizer.canClearItem("formdata", clearDone2);
|
||||
}
|
||||
|
||||
function clearDone2(aItemName, aResult)
|
||||
{
|
||||
is(gFindBar.getElement("findbar-textbox").value, "", "findBar textbox should be empty after sanitize");
|
||||
ok(!aResult, "canClear now false after sanitize");
|
||||
finish();
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Bug 453440 - Test the timespan-based logic of the sanitizer code
|
||||
var now_uSec = Date.now() * 1000;
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
|
||||
const formhist = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
|
||||
|
||||
const kUsecPerMin = 60 * 1000000;
|
||||
|
||||
@ -11,14 +13,50 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
|
||||
|
||||
function promiseFormHistoryRemoved() {
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function onfh() {
|
||||
Services.obs.removeObserver(onfh, "satchel-storage-changed", false);
|
||||
deferred.resolve();
|
||||
}, "satchel-storage-changed", false);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
setupDownloads();
|
||||
setupFormHistory();
|
||||
setupHistory(function() {
|
||||
Task.spawn(onHistoryReady).then(finish);
|
||||
});
|
||||
Task.spawn(function() {
|
||||
setupDownloads();
|
||||
yield setupFormHistory();
|
||||
yield setupHistory();
|
||||
yield onHistoryReady();
|
||||
}).then(finish);
|
||||
}
|
||||
|
||||
function countEntries(name, message, check) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
var obj = {};
|
||||
if (name !== null)
|
||||
obj.fieldname = name;
|
||||
|
||||
let count;
|
||||
FormHistory.count(obj, { handleResult: function (result) count = result,
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
deferred.reject(error)
|
||||
},
|
||||
handleCompletion: function (reason) {
|
||||
if (!reason) {
|
||||
check(count, message);
|
||||
deferred.resolve();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function onHistoryReady() {
|
||||
@ -47,6 +85,8 @@ function onHistoryReady() {
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))),
|
||||
"Pretend visit to 10minutes.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://1hour.com"))),
|
||||
@ -68,16 +108,19 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("10minutes"), "10minutes form entry should be deleted");
|
||||
ok(formhist.nameExists("1hour"), "1hour form entry should still exist");
|
||||
ok(formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
|
||||
ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
let checkZero = function(num, message) { is(num, 0, message); }
|
||||
let checkOne = function(num, message) { is(num, 1, message); }
|
||||
|
||||
yield countEntries("10minutes", "10minutes form entry should be deleted", checkZero);
|
||||
yield countEntries("1hour", "1hour form entry should still exist", checkOne);
|
||||
yield countEntries("1hour10minutes", "1hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("2hour", "2hour form entry should still exist", checkOne);
|
||||
yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("4hour", "4hour form entry should still exist", checkOne);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (minutesSinceMidnight > 10)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555555), "10 minute download should now be deleted");
|
||||
ok(downloadExists(5555551), "<1 hour download should still be present");
|
||||
@ -95,6 +138,8 @@ function onHistoryReady() {
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 1);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))),
|
||||
"Pretend visit to 1hour.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
|
||||
@ -114,15 +159,15 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("1hour"), "1hour form entry should be deleted");
|
||||
ok(formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
|
||||
ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
yield countEntries("1hour", "1hour form entry should be deleted", checkZero);
|
||||
yield countEntries("1hour10minutes", "1hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("2hour", "2hour form entry should still exist", checkOne);
|
||||
yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("4hour", "4hour form entry should still exist", checkOne);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (hoursSinceMidnight > 1)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555551), "<1 hour download should now be deleted");
|
||||
ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
|
||||
@ -140,6 +185,8 @@ function onHistoryReady() {
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
|
||||
"Pretend visit to 1hour10minutes.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
|
||||
@ -157,14 +204,14 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should be deleted");
|
||||
ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
|
||||
ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
yield countEntries("1hour10minutes", "1hour10minutes form entry should be deleted", checkZero);
|
||||
yield countEntries("2hour", "2hour form entry should still exist", checkOne);
|
||||
yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("4hour", "4hour form entry should still exist", checkOne);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (minutesSinceMidnight > 70)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
@ -179,6 +226,8 @@ function onHistoryReady() {
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 2);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))),
|
||||
"Pretend visit to 2hour.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
|
||||
@ -194,15 +243,14 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("2hour"), "2hour form entry should be deleted");
|
||||
ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
|
||||
ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
yield countEntries("2hour", "2hour form entry should be deleted", checkZero);
|
||||
yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
|
||||
yield countEntries("4hour", "4hour form entry should still exist", checkOne);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (hoursSinceMidnight > 2)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
ok(!downloadExists(5555552), "<2 hour old download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
|
||||
@ -216,6 +264,8 @@ function onHistoryReady() {
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
|
||||
"Pretend visit to 2hour10minutes.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
|
||||
@ -229,12 +279,12 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should be deleted");
|
||||
ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
yield countEntries("2hour10minutes", "2hour10minutes form entry should be deleted", checkZero);
|
||||
yield countEntries("4hour", "4hour form entry should still exist", checkOne);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (minutesSinceMidnight > 130)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
@ -247,6 +297,8 @@ function onHistoryReady() {
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 3);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))),
|
||||
"Pretend visit to 4hour.com should now be deleted");
|
||||
ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
|
||||
@ -258,11 +310,11 @@ function onHistoryReady() {
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("4hour"), "4hour form entry should be deleted");
|
||||
ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
|
||||
yield countEntries("4hour", "4hour form entry should be deleted", checkZero);
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
|
||||
if (hoursSinceMidnight > 4)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555553), "<4 hour old download should now be deleted");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
@ -275,6 +327,8 @@ function onHistoryReady() {
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
|
||||
"Pretend visit to 4hour10minutes.com should now be deleted");
|
||||
if (minutesSinceMidnight > 250) {
|
||||
@ -283,12 +337,12 @@ function onHistoryReady() {
|
||||
}
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
|
||||
ok(!formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should be deleted");
|
||||
if (minutesSinceMidnight > 250)
|
||||
ok(formhist.nameExists("today"), "today form entry should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
|
||||
yield countEntries("4hour10minutes", "4hour10minutes form entry should be deleted", checkZero);
|
||||
if (minutesSinceMidnight > 250)
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
if (minutesSinceMidnight > 250)
|
||||
@ -298,6 +352,8 @@ function onHistoryReady() {
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 4);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
// Be careful. If we add our objectss just before midnight, and sanitize
|
||||
// runs immediately after, they won't be expired. This is expected, but
|
||||
// we should not test in that case. We cannot just test for opposite
|
||||
@ -307,28 +363,33 @@ function onHistoryReady() {
|
||||
if (today) {
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))),
|
||||
"Pretend visit to today.com should now be deleted");
|
||||
ok(!formhist.nameExists("today"), "today form entry should be deleted");
|
||||
|
||||
yield countEntries("today", "today form entry should be deleted", checkZero);
|
||||
ok(!downloadExists(5555554), "'Today' download should now be deleted");
|
||||
}
|
||||
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
|
||||
// Choose everything
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 0);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should now be deleted");
|
||||
|
||||
ok(!formhist.nameExists("b4today"), "b4today form entry should be deleted");
|
||||
yield countEntries("b4today", "b4today form entry should be deleted", checkZero);
|
||||
|
||||
ok(!downloadExists(5555550), "Year old download should now be deleted");
|
||||
}
|
||||
|
||||
function setupHistory(aCallback) {
|
||||
function setupHistory() {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let places = [];
|
||||
|
||||
function addPlace(aURI, aTitle, aVisitDate) {
|
||||
@ -363,73 +424,140 @@ function setupHistory(aCallback) {
|
||||
PlacesUtils.asyncHistory.updatePlaces(places, {
|
||||
handleError: function () ok(false, "Unexpected error in adding visit."),
|
||||
handleResult: function () { },
|
||||
handleCompletion: function () aCallback()
|
||||
handleCompletion: function () deferred.resolve()
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function setupFormHistory() {
|
||||
// Make sure we've got a clean DB to start with.
|
||||
formhist.removeAllEntries();
|
||||
|
||||
// Add the entries we'll be testing.
|
||||
formhist.addEntry("10minutes", "10m");
|
||||
formhist.addEntry("1hour", "1h");
|
||||
formhist.addEntry("1hour10minutes", "1h10m");
|
||||
formhist.addEntry("2hour", "2h");
|
||||
formhist.addEntry("2hour10minutes", "2h10m");
|
||||
formhist.addEntry("4hour", "4h");
|
||||
formhist.addEntry("4hour10minutes", "4h10m");
|
||||
formhist.addEntry("today", "1d");
|
||||
formhist.addEntry("b4today", "1y");
|
||||
function searchEntries(terms, params) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let results = [];
|
||||
FormHistory.search(terms, params, { handleResult: function (result) results.push(result),
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { deferred.resolve(results); }
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function update(changes)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
FormHistory.update(changes, { handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { deferred.resolve(); }
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Make sure we've got a clean DB to start with, then add the entries we'll be testing.
|
||||
yield update(
|
||||
[{
|
||||
op: "remove"
|
||||
},
|
||||
{
|
||||
op : "add",
|
||||
fieldname : "10minutes",
|
||||
value : "10m"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "1hour",
|
||||
value : "1h"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "1hour10minutes",
|
||||
value : "1h10m"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "2hour",
|
||||
value : "2h"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "2hour10minutes",
|
||||
value : "2h10m"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "4hour",
|
||||
value : "4h"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "4hour10minutes",
|
||||
value : "4h10m"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "today",
|
||||
value : "1d"
|
||||
}, {
|
||||
op : "add",
|
||||
fieldname : "b4today",
|
||||
value : "1y"
|
||||
}]);
|
||||
|
||||
// Artifically age the entries to the proper vintage.
|
||||
let db = formhist.DBConnection;
|
||||
let timestamp = now_uSec - 10 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '10minutes'");
|
||||
let results = yield searchEntries(["guid"], { fieldname: "10minutes" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 45 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '1hour'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "1hour" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 70 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '1hour10minutes'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "1hour10minutes" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 90 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '2hour'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "2hour" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 130 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '2hour10minutes'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "2hour10minutes" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 180 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '4hour'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "4hour" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
timestamp = now_uSec - 250 * kUsecPerMin;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '4hour10minutes'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "4hour10minutes" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
let today = new Date();
|
||||
today.setHours(0);
|
||||
today.setMinutes(0);
|
||||
today.setSeconds(1);
|
||||
timestamp = today.getTime() * 1000;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = 'today'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "today" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
let lastYear = new Date();
|
||||
lastYear.setFullYear(lastYear.getFullYear() - 1);
|
||||
timestamp = lastYear.getTime() * 1000;
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = 'b4today'");
|
||||
results = yield searchEntries(["guid"], { fieldname: "b4today" });
|
||||
yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
|
||||
|
||||
var checks = 0;
|
||||
let checkOne = function(num, message) { is(num, 1, message); checks++; }
|
||||
|
||||
// Sanity check.
|
||||
ok(formhist.nameExists("10minutes"), "Checking for 10minutes form history entry creation");
|
||||
ok(formhist.nameExists("1hour"), "Checking for 1hour form history entry creation");
|
||||
ok(formhist.nameExists("1hour10minutes"), "Checking for 1hour10minutes form history entry creation");
|
||||
ok(formhist.nameExists("2hour"), "Checking for 2hour form history entry creation");
|
||||
ok(formhist.nameExists("2hour10minutes"), "Checking for 2hour10minutes form history entry creation");
|
||||
ok(formhist.nameExists("4hour"), "Checking for 4hour form history entry creation");
|
||||
ok(formhist.nameExists("4hour10minutes"), "Checking for 4hour10minutes form history entry creation");
|
||||
ok(formhist.nameExists("today"), "Checking for today form history entry creation");
|
||||
ok(formhist.nameExists("b4today"), "Checking for b4today form history entry creation");
|
||||
yield countEntries("10minutes", "Checking for 10minutes form history entry creation", checkOne);
|
||||
yield countEntries("1hour", "Checking for 1hour form history entry creation", checkOne);
|
||||
yield countEntries("1hour10minutes", "Checking for 1hour10minutes form history entry creation", checkOne);
|
||||
yield countEntries("2hour", "Checking for 2hour form history entry creation", checkOne);
|
||||
yield countEntries("2hour10minutes", "Checking for 2hour10minutes form history entry creation", checkOne);
|
||||
yield countEntries("4hour", "Checking for 4hour form history entry creation", checkOne);
|
||||
yield countEntries("4hour10minutes", "Checking for 4hour10minutes form history entry creation", checkOne);
|
||||
yield countEntries("today", "Checking for today form history entry creation", checkOne);
|
||||
yield countEntries("b4today", "Checking for b4today form history entry creation", checkOne);
|
||||
is(checks, 9, "9 checks made");
|
||||
}
|
||||
|
||||
function setupDownloads() {
|
||||
|
@ -17,6 +17,11 @@
|
||||
* browser/base/content/test/browser_sanitize-timespans.js.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
@ -24,11 +29,11 @@ let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
const formhist = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
const kUsecPerMin = 60 * 1000000;
|
||||
|
||||
let formEntries;
|
||||
|
||||
// Add tests here. Each is a function that's called by doNextTest().
|
||||
var gAllTests = [
|
||||
|
||||
@ -80,7 +85,7 @@ var gAllTests = [
|
||||
};
|
||||
wh.onunload = function () {
|
||||
yield promiseHistoryClearedState(uris, false);
|
||||
blankSlate();
|
||||
yield blankSlate();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
};
|
||||
wh.open();
|
||||
@ -148,7 +153,7 @@ var gAllTests = [
|
||||
ensureDownloadsClearedState(olderDownloadIDs, false);
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
blankSlate();
|
||||
yield blankSlate();
|
||||
yield promiseHistoryClearedState(olderURIs, true);
|
||||
ensureDownloadsClearedState(olderDownloadIDs, true);
|
||||
};
|
||||
@ -156,6 +161,23 @@ var gAllTests = [
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add form history entries for the next test.
|
||||
*/
|
||||
function () {
|
||||
formEntries = [];
|
||||
|
||||
let iter = function() {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
formEntries.push(addFormEntryWithMinutesAgo(iter, i));
|
||||
yield;
|
||||
}
|
||||
doNextTest();
|
||||
}();
|
||||
|
||||
iter.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the combined history-downloads checkbox removes neither
|
||||
* history visits nor downloads when not checked.
|
||||
@ -176,10 +198,6 @@ var gAllTests = [
|
||||
for (let i = 0; i < 5; i++) {
|
||||
downloadIDs.push(addDownloadWithMinutesAgo(i));
|
||||
}
|
||||
let formEntries = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
formEntries.push(addFormEntryWithMinutesAgo(i));
|
||||
}
|
||||
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function () {
|
||||
@ -207,10 +225,14 @@ var gAllTests = [
|
||||
// Of the three only form entries should be cleared.
|
||||
yield promiseHistoryClearedState(uris, false);
|
||||
ensureDownloadsClearedState(downloadIDs, false);
|
||||
ensureFormEntriesClearedState(formEntries, true);
|
||||
|
||||
formEntries.forEach(function (entry) {
|
||||
let exists = yield formNameExists(entry);
|
||||
is(exists, false, "form entry " + entry + " should no longer exist");
|
||||
});
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
blankSlate();
|
||||
yield blankSlate();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
};
|
||||
@ -301,6 +323,19 @@ var gAllTests = [
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Add form history entry for the next test.
|
||||
*/
|
||||
function () {
|
||||
let iter = function() {
|
||||
formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
|
||||
yield;
|
||||
doNextTest();
|
||||
}();
|
||||
|
||||
iter.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* The next three tests checks that when a certain history item cannot be
|
||||
* cleared then the checkbox should be both disabled and unchecked.
|
||||
@ -311,7 +346,6 @@ var gAllTests = [
|
||||
let pURI = makeURI("http://" + 10 + "-minutes-ago.com/");
|
||||
addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}, function() {
|
||||
let uris = [ pURI ];
|
||||
let formEntries = [ addFormEntryWithMinutesAgo(10) ];
|
||||
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function() {
|
||||
@ -331,7 +365,9 @@ var gAllTests = [
|
||||
};
|
||||
wh.onunload = function () {
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureFormEntriesClearedState(formEntries, true);
|
||||
|
||||
let exists = yield formNameExists(formEntries[0]);
|
||||
is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
|
||||
};
|
||||
wh.open();
|
||||
});
|
||||
@ -365,9 +401,21 @@ var gAllTests = [
|
||||
}
|
||||
wh.open();
|
||||
},
|
||||
function () {
|
||||
let formEntries = [ addFormEntryWithMinutesAgo(10) ];
|
||||
|
||||
/**
|
||||
* Add form history entry for the next test.
|
||||
*/
|
||||
function () {
|
||||
let iter = function() {
|
||||
formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
|
||||
yield;
|
||||
doNextTest();
|
||||
}();
|
||||
|
||||
iter.next();
|
||||
},
|
||||
|
||||
function () {
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function() {
|
||||
boolPrefIs("cpd.formdata", true,
|
||||
@ -383,7 +431,8 @@ var gAllTests = [
|
||||
this.acceptDialog();
|
||||
};
|
||||
wh.onunload = function () {
|
||||
ensureFormEntriesClearedState(formEntries, true);
|
||||
let exists = yield formNameExists(formEntries[0]);
|
||||
is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
|
||||
};
|
||||
wh.open();
|
||||
},
|
||||
@ -739,7 +788,9 @@ WindowHelper.prototype = {
|
||||
* Opens the clear recent history dialog. Before calling this, set
|
||||
* this.onload to a function to execute onload. It should close the dialog
|
||||
* when done so that the tests may continue. Set this.onunload to a function
|
||||
* to execute onunload. this.onunload is optional.
|
||||
* to execute onunload. this.onunload is optional. If it returns true, the
|
||||
* caller is expected to call waitForAsyncUpdates at some point; if false is
|
||||
* returned, waitForAsyncUpdates is called automatically.
|
||||
*/
|
||||
open: function () {
|
||||
let wh = this;
|
||||
@ -891,28 +942,59 @@ function addDownloadWithMinutesAgo(aMinutesAgo) {
|
||||
* @param aMinutesAgo
|
||||
* The entry will be added this many minutes ago
|
||||
*/
|
||||
function addFormEntryWithMinutesAgo(aMinutesAgo) {
|
||||
function addFormEntryWithMinutesAgo(then, aMinutesAgo) {
|
||||
let name = aMinutesAgo + "-minutes-ago";
|
||||
formhist.addEntry(name, "dummy");
|
||||
|
||||
// Artifically age the entry to the proper vintage.
|
||||
let db = formhist.DBConnection;
|
||||
let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin);
|
||||
db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
|
||||
timestamp + " WHERE fieldname = '" + name + "'");
|
||||
|
||||
is(formhist.nameExists(name), true,
|
||||
"Sanity check: form entry " + name + " should exist after creating it");
|
||||
FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp },
|
||||
{ handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { then.next(); }
|
||||
});
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a form entry exists.
|
||||
*/
|
||||
function formNameExists(name)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let count = 0;
|
||||
FormHistory.count({ fieldname: name },
|
||||
{ handleResult: function (result) count = result,
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) {
|
||||
if (!reason) deferred.resolve(count);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all history visits, downloads, and form entries.
|
||||
*/
|
||||
function blankSlate() {
|
||||
PlacesUtils.bhistory.removeAllPages();
|
||||
dm.cleanUp();
|
||||
formhist.removeAllEntries();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
FormHistory.update({ op: "remove" },
|
||||
{ handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -982,22 +1064,6 @@ function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the specified form entries are either cleared or not.
|
||||
*
|
||||
* @param aFormEntries
|
||||
* Array of form entry names
|
||||
* @param aShouldBeCleared
|
||||
* True if each form entry should be cleared, false otherwise
|
||||
*/
|
||||
function ensureFormEntriesClearedState(aFormEntries, aShouldBeCleared) {
|
||||
let niceStr = aShouldBeCleared ? "no longer" : "still";
|
||||
aFormEntries.forEach(function (entry) {
|
||||
is(formhist.nameExists(entry), !aShouldBeCleared,
|
||||
"form entry " + entry + " should " + niceStr + " exist");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the given pref is the expected value.
|
||||
*
|
||||
@ -1026,8 +1092,8 @@ function visitTimeForMinutesAgo(aMinutesAgo) {
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
blankSlate();
|
||||
waitForExplicitFinish();
|
||||
blankSlate();
|
||||
// Kick off all the tests in the gAllTests array.
|
||||
waitForAsyncUpdates(doNextTest);
|
||||
}
|
||||
|
@ -30,6 +30,12 @@ const UNEXPECTED_NOTIFICATIONS = [
|
||||
|
||||
const URL = "ftp://localhost/clearHistoryOnShutdown/";
|
||||
|
||||
// Send the profile-after-change notification to the form history component to ensure
|
||||
// that it has been initialized.
|
||||
var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
formHistoryStartup.observe(null, "profile-after-change", null);
|
||||
|
||||
let notificationIndex = 0;
|
||||
|
||||
let notificationsObserver = {
|
||||
|
@ -109,6 +109,9 @@
|
||||
"anonid", "searchbar-popup");</field>
|
||||
<field name="_ss">null</field>
|
||||
<field name="_engines">null</field>
|
||||
<field name="FormHistory" readonly="true">
|
||||
(Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
|
||||
</field>
|
||||
|
||||
<property name="engines" readonly="true">
|
||||
<getter><![CDATA[
|
||||
@ -455,12 +458,13 @@
|
||||
|
||||
// Save the current value in the form history
|
||||
if (textValue && !PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
try {
|
||||
textBox._formHistSvc.addEntry(textBox.getAttribute("autocompletesearchparam"),
|
||||
textValue);
|
||||
} catch (ex) {
|
||||
Components.utils.reportError("Saving search to form history failed: " + ex);
|
||||
}
|
||||
this.FormHistory.update(
|
||||
{ op : "bump",
|
||||
fieldname : textBox.getAttribute("autocompletesearchparam"),
|
||||
value : textValue },
|
||||
{ handleError : function(aError) {
|
||||
Components.utils.reportError("Saving search to form history failed: " + aError.message);
|
||||
}});
|
||||
}
|
||||
|
||||
this.doSearch(textValue, where);
|
||||
@ -554,7 +558,6 @@
|
||||
]]></destructor>
|
||||
|
||||
<field name="_stringBundle"/>
|
||||
<field name="_formHistSvc"/>
|
||||
<field name="_prefBranch"/>
|
||||
<field name="_suggestMenuItem"/>
|
||||
<field name="_suggestEnabled"/>
|
||||
@ -565,9 +568,6 @@
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
// Initialize fields
|
||||
this._stringBundle = document.getBindingParent(this)._stringBundle;
|
||||
this._formHistSvc =
|
||||
Components.classes["@mozilla.org/satchel/form-history;1"]
|
||||
.getService(Components.interfaces.nsIFormHistory2);
|
||||
this._prefBranch =
|
||||
Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
@ -748,10 +748,6 @@
|
||||
},
|
||||
|
||||
isCommandEnabled: function(aCommand) {
|
||||
if (aCommand == "cmd_clearhistory") {
|
||||
var param = this._self.getAttribute("autocompletesearchparam");
|
||||
return this._self._formHistSvc.nameExists(param);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -759,7 +755,10 @@
|
||||
switch (aCommand) {
|
||||
case "cmd_clearhistory":
|
||||
var param = this._self.getAttribute("autocompletesearchparam");
|
||||
this._self._formHistSvc.removeEntriesForName(param);
|
||||
|
||||
let searchBar = this._self.parentNode;
|
||||
|
||||
BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null);
|
||||
this._self.value = "";
|
||||
break;
|
||||
case "cmd_togglesuggest":
|
||||
|
@ -5,6 +5,9 @@ this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
@ -20,6 +23,8 @@ function test() {
|
||||
|
||||
var ss = Services.search;
|
||||
|
||||
let testIterator;
|
||||
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "engine-added":
|
||||
@ -182,33 +187,64 @@ function test() {
|
||||
content.location.href = "about:blank";
|
||||
simulateClick({ button: 2 }, searchButton);
|
||||
setTimeout(function() {
|
||||
|
||||
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
|
||||
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
|
||||
|
||||
testSearchHistory();
|
||||
testIterator = testSearchHistory();
|
||||
testIterator.next();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function countEntries(name, value, message) {
|
||||
let count = 0;
|
||||
FormHistory.count({ fieldname: name, value: value },
|
||||
{ handleResult: function(result) { count = result; },
|
||||
handleError: function(error) { throw error; },
|
||||
handleCompletion: function(reason) {
|
||||
if (!reason) {
|
||||
ok(count > 0, message);
|
||||
testIterator.next();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testSearchHistory() {
|
||||
var textbox = searchBar._textbox;
|
||||
for (var i = 0; i < searchEntries.length; i++) {
|
||||
let exists = textbox._formHistSvc.entryExists(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
|
||||
ok(exists, "form history entry '" + searchEntries[i] + "' should exist");
|
||||
yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i],
|
||||
"form history entry '" + searchEntries[i] + "' should exist");
|
||||
}
|
||||
testAutocomplete();
|
||||
}
|
||||
|
||||
function testAutocomplete() {
|
||||
var popup = searchBar.textbox.popup;
|
||||
popup.addEventListener("popupshowing", function testACPopupShowing() {
|
||||
popup.removeEventListener("popupshowing", testACPopupShowing);
|
||||
popup.addEventListener("popupshown", function testACPopupShowing() {
|
||||
popup.removeEventListener("popupshown", testACPopupShowing);
|
||||
checkMenuEntries(searchEntries);
|
||||
SimpleTest.executeSoon(finalize);
|
||||
testClearHistory();
|
||||
});
|
||||
searchBar.textbox.showHistoryPopup();
|
||||
}
|
||||
|
||||
function testClearHistory() {
|
||||
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
|
||||
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
|
||||
controller.doCommand("cmd_clearhistory");
|
||||
let count = 0;
|
||||
FormHistory.count({ },
|
||||
{ handleResult: function(result) { count = result; },
|
||||
handleError: function(error) { throw error; },
|
||||
handleCompletion: function(reason) {
|
||||
if (!reason) {
|
||||
ok(count == 0, "History cleared");
|
||||
finalize();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function finalize() {
|
||||
searchBar.value = "";
|
||||
while (gBrowser.tabs.length != 1) {
|
||||
|
@ -432,6 +432,7 @@
|
||||
@BINPATH@/components/satchel.manifest
|
||||
@BINPATH@/components/nsFormAutoComplete.js
|
||||
@BINPATH@/components/nsFormHistory.js
|
||||
@BINPATH@/components/FormHistoryStartup.js
|
||||
@BINPATH@/components/nsInputListAutoComplete.js
|
||||
@BINPATH@/components/contentSecurityPolicy.manifest
|
||||
@BINPATH@/components/contentSecurityPolicy.js
|
||||
|
@ -37,7 +37,7 @@ SimpleTest.waitForFocus(function() {
|
||||
function listener() {
|
||||
popupShown = true;
|
||||
}
|
||||
SpecialPowers.addAutoCompletePopupEventListener(window, listener);
|
||||
SpecialPowers.addAutoCompletePopupEventListener(window, "popupshowing", listener);
|
||||
|
||||
var event = document.createEvent("KeyboardEvent");
|
||||
|
||||
@ -49,7 +49,7 @@ SimpleTest.waitForFocus(function() {
|
||||
|
||||
hitEventLoop(function() {
|
||||
ok(!popupShown, "Popup must not be opened");
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, listener);
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshowing", listener);
|
||||
SimpleTest.finish();
|
||||
}, 100);
|
||||
}, 0);
|
||||
|
@ -4667,7 +4667,7 @@ var FormAssistant = {
|
||||
|
||||
if (this._showValidationMessage(focused))
|
||||
break;
|
||||
this._showAutoCompleteSuggestions(focused);
|
||||
this._showAutoCompleteSuggestions(focused, function () {});
|
||||
} else {
|
||||
// temporarily hide the form assist popup while we're panning or zooming the page
|
||||
this._hideFormAssistPopup();
|
||||
@ -4737,8 +4737,14 @@ var FormAssistant = {
|
||||
// only be available if an invalid form was submitted)
|
||||
if (this._showValidationMessage(currentElement))
|
||||
break;
|
||||
if (!this._showAutoCompleteSuggestions(currentElement))
|
||||
this._hideFormAssistPopup();
|
||||
|
||||
let checkResultsClick = hasResults => {
|
||||
if (!hasResults) {
|
||||
this._hideFormAssistPopup();
|
||||
}
|
||||
};
|
||||
|
||||
this._showAutoCompleteSuggestions(currentElement, checkResultsClick);
|
||||
break;
|
||||
|
||||
case "input":
|
||||
@ -4746,13 +4752,18 @@ var FormAssistant = {
|
||||
|
||||
// Since we can only show one popup at a time, prioritze autocomplete
|
||||
// suggestions over a form validation message
|
||||
if (this._showAutoCompleteSuggestions(currentElement))
|
||||
break;
|
||||
if (this._showValidationMessage(currentElement))
|
||||
break;
|
||||
let checkResultsInput = hasResults => {
|
||||
if (hasResults)
|
||||
return;
|
||||
|
||||
// If we're not showing autocomplete suggestions, hide the form assist popup
|
||||
this._hideFormAssistPopup();
|
||||
if (!this._showValidationMessage(currentElement))
|
||||
return;
|
||||
|
||||
// If we're not showing autocomplete suggestions, hide the form assist popup
|
||||
this._hideFormAssistPopup();
|
||||
};
|
||||
|
||||
this._showAutoCompleteSuggestions(currentElement, checkResultsInput);
|
||||
break;
|
||||
|
||||
// Reset invalid submit state on each pageshow
|
||||
@ -4776,27 +4787,31 @@ var FormAssistant = {
|
||||
},
|
||||
|
||||
// Retrieves autocomplete suggestions for an element from the form autocomplete service.
|
||||
_getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement) {
|
||||
// aCallback(array_of_suggestions) is called when results are available.
|
||||
_getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement, aCallback) {
|
||||
// Cache the form autocomplete service for future use
|
||||
if (!this._formAutoCompleteService)
|
||||
this._formAutoCompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].
|
||||
getService(Ci.nsIFormAutoComplete);
|
||||
|
||||
let results = this._formAutoCompleteService.autoCompleteSearch(aElement.name || aElement.id,
|
||||
aSearchString, aElement, null);
|
||||
let suggestions = [];
|
||||
for (let i = 0; i < results.matchCount; i++) {
|
||||
let value = results.getValueAt(i);
|
||||
let resultsAvailable = function (results) {
|
||||
let suggestions = [];
|
||||
for (let i = 0; i < results.matchCount; i++) {
|
||||
let value = results.getValueAt(i);
|
||||
|
||||
// Do not show the value if it is the current one in the input field
|
||||
if (value == aSearchString)
|
||||
continue;
|
||||
// Do not show the value if it is the current one in the input field
|
||||
if (value == aSearchString)
|
||||
continue;
|
||||
|
||||
// Supply a label and value, since they can differ for datalist suggestions
|
||||
suggestions.push({ label: value, value: value });
|
||||
}
|
||||
// Supply a label and value, since they can differ for datalist suggestions
|
||||
suggestions.push({ label: value, value: value });
|
||||
aCallback(suggestions);
|
||||
}
|
||||
};
|
||||
|
||||
return suggestions;
|
||||
this._formAutoCompleteService.autoCompleteSearchAsync(aElement.name || aElement.id,
|
||||
aSearchString, aElement, null,
|
||||
resultsAvailable);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -4833,40 +4848,47 @@ var FormAssistant = {
|
||||
},
|
||||
|
||||
// Retrieves autocomplete suggestions for an element from the form autocomplete service
|
||||
// and sends the suggestions to the Java UI, along with element position data.
|
||||
// Returns true if there are suggestions to show, false otherwise.
|
||||
_showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement) {
|
||||
if (!this._isAutoComplete(aElement))
|
||||
return false;
|
||||
// and sends the suggestions to the Java UI, along with element position data. As
|
||||
// autocomplete queries are asynchronous, calls aCallback when done with a true
|
||||
// argument if results were found and false if no results were found.
|
||||
_showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement, aCallback) {
|
||||
if (!this._isAutoComplete(aElement)) {
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't display the form auto-complete popup after the user starts typing
|
||||
// to avoid confusing somes IME. See bug 758820 and bug 632744.
|
||||
if (this._isBlocklisted && aElement.value.length > 0) {
|
||||
return false;
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement);
|
||||
let listSuggestions = this._getListSuggestions(aElement);
|
||||
let resultsAvailable = autoCompleteSuggestions => {
|
||||
// On desktop, we show datalist suggestions below autocomplete suggestions,
|
||||
// without duplicates removed.
|
||||
let listSuggestions = this._getListSuggestions(aElement);
|
||||
let suggestions = autoCompleteSuggestions.concat(listSuggestions);
|
||||
|
||||
// On desktop, we show datalist suggestions below autocomplete suggestions,
|
||||
// without duplicates removed.
|
||||
let suggestions = autoCompleteSuggestions.concat(listSuggestions);
|
||||
// Return false if there are no suggestions to show
|
||||
if (!suggestions.length) {
|
||||
aCallback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Return false if there are no suggestions to show
|
||||
if (!suggestions.length)
|
||||
return false;
|
||||
sendMessageToJava({
|
||||
type: "FormAssist:AutoComplete",
|
||||
suggestions: suggestions,
|
||||
rect: ElementTouchHelper.getBoundingContentRect(aElement)
|
||||
});
|
||||
|
||||
sendMessageToJava({
|
||||
type: "FormAssist:AutoComplete",
|
||||
suggestions: suggestions,
|
||||
rect: ElementTouchHelper.getBoundingContentRect(aElement)
|
||||
});
|
||||
// Keep track of input element so we can fill it in if the user
|
||||
// selects an autocomplete suggestion
|
||||
this._currentInputElement = aElement;
|
||||
aCallback(true);
|
||||
};
|
||||
|
||||
// Keep track of input element so we can fill it in if the user
|
||||
// selects an autocomplete suggestion
|
||||
this._currentInputElement = aElement;
|
||||
|
||||
return true;
|
||||
this._getAutoCompleteSuggestions(aElement.value, aElement, resultsAvailable);
|
||||
},
|
||||
|
||||
// Only show a validation message if the user submitted an invalid form,
|
||||
|
@ -345,6 +345,7 @@
|
||||
@BINPATH@/components/satchel.manifest
|
||||
@BINPATH@/components/nsFormAutoComplete.js
|
||||
@BINPATH@/components/nsFormHistory.js
|
||||
@BINPATH@/components/FormHistoryStartup.js
|
||||
@BINPATH@/components/nsInputListAutoComplete.js
|
||||
@BINPATH@/components/contentSecurityPolicy.manifest
|
||||
@BINPATH@/components/contentSecurityPolicy.js
|
||||
|
@ -962,16 +962,21 @@ SpecialPowersAPI.prototype = {
|
||||
return this._getTopChromeWindow(window).document
|
||||
.getElementById("PopupAutoComplete");
|
||||
},
|
||||
addAutoCompletePopupEventListener: function(window, listener) {
|
||||
this._getAutoCompletePopup(window).addEventListener("popupshowing",
|
||||
addAutoCompletePopupEventListener: function(window, eventname, listener) {
|
||||
this._getAutoCompletePopup(window).addEventListener(eventname,
|
||||
listener,
|
||||
false);
|
||||
},
|
||||
removeAutoCompletePopupEventListener: function(window, listener) {
|
||||
this._getAutoCompletePopup(window).removeEventListener("popupshowing",
|
||||
removeAutoCompletePopupEventListener: function(window, eventname, listener) {
|
||||
this._getAutoCompletePopup(window).removeEventListener(eventname,
|
||||
listener,
|
||||
false);
|
||||
},
|
||||
get formHistory() {
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/FormHistory.jsm", tmp);
|
||||
return wrapPrivileged(tmp.FormHistory);
|
||||
},
|
||||
getFormFillController: function(window) {
|
||||
return Components.classes["@mozilla.org/satchel/form-fill-controller;1"]
|
||||
.getService(Components.interfaces.nsIFormFillController);
|
||||
|
1089
toolkit/components/satchel/FormHistory.jsm
Normal file
1089
toolkit/components/satchel/FormHistory.jsm
Normal file
File diff suppressed because it is too large
Load Diff
78
toolkit/components/satchel/FormHistoryStartup.js
Normal file
78
toolkit/components/satchel/FormHistoryStartup.js
Normal file
@ -0,0 +1,78 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function FormHistoryStartup() { }
|
||||
|
||||
FormHistoryStartup.prototype = {
|
||||
classID: Components.ID("{3A0012EB-007F-4BB8-AA81-A07385F77A25}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsIFrameMessageListener
|
||||
]),
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "nsPref:changed":
|
||||
FormHistory.updatePrefs();
|
||||
break;
|
||||
case "idle-daily":
|
||||
case "formhistory-expire-now":
|
||||
FormHistory.expireOldEntries();
|
||||
break;
|
||||
case "profile-before-change":
|
||||
FormHistory.shutdown();
|
||||
break;
|
||||
case "profile-after-change":
|
||||
this.init();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
inited: false,
|
||||
|
||||
init: function()
|
||||
{
|
||||
if (this.inited)
|
||||
return;
|
||||
this.inited = true;
|
||||
|
||||
Services.prefs.addObserver("browser.formfill.", this, true);
|
||||
|
||||
// triggers needed service cleanup and db shutdown
|
||||
Services.obs.addObserver(this, "profile-before-change", true);
|
||||
Services.obs.addObserver(this, "formhistory-expire-now", true);
|
||||
|
||||
let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
|
||||
getService(Ci.nsIMessageListenerManager);
|
||||
messageManager.loadFrameScript("chrome://satchel/content/formSubmitListener.js", true);
|
||||
messageManager.addMessageListener("FormHistory:FormSubmitEntries", this);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
let entries = message.json;
|
||||
let changes = entries.map(function(entry) {
|
||||
return {
|
||||
op : "bump",
|
||||
fieldname : entry.name,
|
||||
value : entry.value,
|
||||
}
|
||||
});
|
||||
|
||||
FormHistory.update(changes);
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FormHistoryStartup]);
|
@ -25,6 +25,7 @@ CPPSRCS = \
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
nsFormAutoComplete.js \
|
||||
FormHistoryStartup.js \
|
||||
nsInputListAutoComplete.js \
|
||||
satchel.manifest \
|
||||
$(NULL)
|
||||
@ -37,4 +38,8 @@ EXTRA_JS_MODULES = \
|
||||
nsFormAutoCompleteResult.jsm \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
FormHistory.jsm \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -10,6 +10,11 @@ const Cr = Components.results;
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
|
||||
"resource://gre/modules/Deprecated.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function FormAutoComplete() {
|
||||
this.init();
|
||||
}
|
||||
@ -18,14 +23,6 @@ FormAutoComplete.prototype = {
|
||||
classID : Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"),
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]),
|
||||
|
||||
__formHistory : null,
|
||||
get _formHistory() {
|
||||
if (!this.__formHistory)
|
||||
this.__formHistory = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
return this.__formHistory;
|
||||
},
|
||||
|
||||
_prefBranch : null,
|
||||
_debug : true, // mirrors browser.formfill.debug
|
||||
_enabled : true, // mirrors browser.formfill.enable preference
|
||||
@ -37,6 +34,13 @@ FormAutoComplete.prototype = {
|
||||
_boundaryWeight : 25,
|
||||
_prefixWeight : 5,
|
||||
|
||||
// Only one query is performed at a time, which will be stored in _pendingQuery
|
||||
// while the query is being performed. It will be cleared when the query finishes,
|
||||
// is cancelled, or an error occurs. If a new query occurs while one is already
|
||||
// pending, the existing one is cancelled. The pending query will be an
|
||||
// mozIStoragePendingStatement object.
|
||||
_pendingQuery : null,
|
||||
|
||||
init : function() {
|
||||
// Preferences. Add observer so we get notified of changes.
|
||||
this._prefBranch = Services.prefs.getBranch("browser.formfill.");
|
||||
@ -50,10 +54,6 @@ FormAutoComplete.prototype = {
|
||||
this._maxTimeGroupings = this._prefBranch.getIntPref("maxTimeGroupings");
|
||||
this._timeGroupingSize = this._prefBranch.getIntPref("timeGroupingSize") * 1000 * 1000;
|
||||
this._expireDays = this._prefBranch.getIntPref("expire_days");
|
||||
|
||||
this._dbStmts = {};
|
||||
|
||||
Services.obs.addObserver(this.observer, "profile-before-change", true);
|
||||
},
|
||||
|
||||
observer : {
|
||||
@ -96,12 +96,6 @@ FormAutoComplete.prototype = {
|
||||
default:
|
||||
self.log("Oops! Pref not handled, change ignored.");
|
||||
}
|
||||
} else if (topic == "profile-before-change") {
|
||||
for each (let stmt in self._dbStmts) {
|
||||
stmt.finalize();
|
||||
}
|
||||
self._dbStmts = {};
|
||||
self.__formHistory = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -121,33 +115,64 @@ FormAutoComplete.prototype = {
|
||||
},
|
||||
|
||||
|
||||
autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) {
|
||||
Deprecated.warning("nsIFormAutoComplete::autoCompleteSearch is deprecated", "https://bugzilla.mozilla.org/show_bug.cgi?id=697377");
|
||||
|
||||
let result = null;
|
||||
let listener = {
|
||||
onSearchCompletion: function (r) result = r
|
||||
};
|
||||
this._autoCompleteSearchShared(aInputName, aUntrimmedSearchString, aField, aPreviousResult, listener);
|
||||
|
||||
// Just wait for the result to to be available.
|
||||
let thread = Components.classes["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
while (!result && this._pendingQuery) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) {
|
||||
this._autoCompleteSearchShared(aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener);
|
||||
},
|
||||
|
||||
/*
|
||||
* autoCompleteSearch
|
||||
* autoCompleteSearchShared
|
||||
*
|
||||
* aInputName -- |name| attribute from the form input being autocompleted.
|
||||
* aUntrimmedSearchString -- current value of the input
|
||||
* aField -- nsIDOMHTMLInputElement being autocompleted (may be null if from chrome)
|
||||
* aPreviousResult -- previous search result, if any.
|
||||
*
|
||||
* Returns: an nsIAutoCompleteResult
|
||||
* aListener -- nsIFormAutoCompleteObserver that listens for the nsIAutoCompleteResult
|
||||
* that may be returned asynchronously.
|
||||
*/
|
||||
autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) {
|
||||
_autoCompleteSearchShared : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) {
|
||||
function sortBytotalScore (a, b) {
|
||||
return b.totalScore - a.totalScore;
|
||||
}
|
||||
|
||||
if (!this._enabled)
|
||||
return null;
|
||||
let result = null;
|
||||
if (!this._enabled) {
|
||||
result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString);
|
||||
if (aListener) {
|
||||
aListener.onSearchCompletion(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// don't allow form inputs (aField != null) to get results from search bar history
|
||||
if (aInputName == 'searchbar-history' && aField) {
|
||||
this.log('autoCompleteSearch for input name "' + aInputName + '" is denied');
|
||||
return null;
|
||||
result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString);
|
||||
if (aListener) {
|
||||
aListener.onSearchCompletion(result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.log("AutoCompleteSearch invoked. Search is: " + aUntrimmedSearchString);
|
||||
let searchString = aUntrimmedSearchString.trim().toLowerCase();
|
||||
let result = null;
|
||||
|
||||
// reuse previous results if:
|
||||
// a) length greater than one character (others searches are special cases) AND
|
||||
@ -176,145 +201,80 @@ FormAutoComplete.prototype = {
|
||||
}
|
||||
filteredEntries.sort(sortBytotalScore);
|
||||
result.wrappedJSObject.entries = filteredEntries;
|
||||
|
||||
if (aListener) {
|
||||
aListener.onSearchCompletion(result);
|
||||
}
|
||||
} else {
|
||||
this.log("Creating new autocomplete search result.");
|
||||
let entries = this.getAutoCompleteValues(aInputName, searchString);
|
||||
result = new FormAutoCompleteResult(this._formHistory, entries, aInputName, aUntrimmedSearchString);
|
||||
if (aField && aField.maxLength > -1) {
|
||||
let original = result.wrappedJSObject.entries;
|
||||
let filtered = original.filter(function (el) el.text.length <= this.maxLength, aField);
|
||||
result.wrappedJSObject.entries = filtered;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
// Start with an empty list.
|
||||
result = new FormAutoCompleteResult(FormHistory, [], aInputName, aUntrimmedSearchString);
|
||||
|
||||
let processEntry = function(aEntries) {
|
||||
if (aField && aField.maxLength > -1) {
|
||||
result.entries =
|
||||
aEntries.filter(function (el) { return el.text.length <= aField.maxLength; });
|
||||
} else {
|
||||
result.entries = aEntries;
|
||||
}
|
||||
|
||||
if (aListener) {
|
||||
aListener.onSearchCompletion(result);
|
||||
}
|
||||
}
|
||||
|
||||
this.getAutoCompleteValues(aInputName, searchString, processEntry);
|
||||
}
|
||||
},
|
||||
|
||||
getAutoCompleteValues : function (fieldName, searchString) {
|
||||
let values = [];
|
||||
let searchTokens;
|
||||
stopAutoCompleteSearch : function () {
|
||||
if (this._pendingQuery) {
|
||||
this._pendingQuery.cancel();
|
||||
this._pendingQuery = null;
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the values for an autocomplete list given a search string.
|
||||
*
|
||||
* fieldName - fieldname field within form history (the form input name)
|
||||
* searchString - string to search for
|
||||
* callback - called when the values are available. Passed an array of objects,
|
||||
* containing properties for each result. The callback is only called
|
||||
* when successful.
|
||||
*/
|
||||
getAutoCompleteValues : function (fieldName, searchString, callback) {
|
||||
let params = {
|
||||
agedWeight: this._agedWeight,
|
||||
bucketSize: this._bucketSize,
|
||||
expiryDate: 1000 * (Date.now() - this._expireDays * 24 * 60 * 60 * 1000),
|
||||
fieldname: fieldName,
|
||||
maxTimeGroupings: this._maxTimeGroupings,
|
||||
now: Date.now() * 1000, // convert from ms to microseconds
|
||||
timeGroupingSize: this._timeGroupingSize
|
||||
timeGroupingSize: this._timeGroupingSize,
|
||||
prefixWeight: this._prefixWeight,
|
||||
boundaryWeight: this._boundaryWeight
|
||||
}
|
||||
|
||||
// only do substring matching when more than one character is typed
|
||||
let where = ""
|
||||
let boundaryCalc = "";
|
||||
if (searchString.length > 1) {
|
||||
searchTokens = searchString.split(/\s+/);
|
||||
this.stopAutoCompleteSearch();
|
||||
|
||||
// build up the word boundary and prefix match bonus calculation
|
||||
boundaryCalc = "MAX(1, :prefixWeight * (value LIKE :valuePrefix ESCAPE '/') + (";
|
||||
// for each word, calculate word boundary weights for the SELECT clause and
|
||||
// add word to the WHERE clause of the query
|
||||
let tokenCalc = [];
|
||||
for (let i = 0; i < searchTokens.length; i++) {
|
||||
tokenCalc.push("(value LIKE :tokenBegin" + i + " ESCAPE '/') + " +
|
||||
"(value LIKE :tokenBoundary" + i + " ESCAPE '/')");
|
||||
where += "AND (value LIKE :tokenContains" + i + " ESCAPE '/') ";
|
||||
let results = [];
|
||||
let processResults = {
|
||||
handleResult: aResult => {
|
||||
results.push(aResult);
|
||||
},
|
||||
handleError: aError => {
|
||||
this.log("getAutocompleteValues failed: " + aError.message);
|
||||
},
|
||||
handleCompletion: aReason => {
|
||||
this._pendingQuery = null;
|
||||
if (!aReason) {
|
||||
callback(results);
|
||||
}
|
||||
// add more weight if we have a traditional prefix match and
|
||||
// multiply boundary bonuses by boundary weight
|
||||
boundaryCalc += tokenCalc.join(" + ") + ") * :boundaryWeight)";
|
||||
params.prefixWeight = this._prefixWeight;
|
||||
params.boundaryWeight = this._boundaryWeight;
|
||||
} else if (searchString.length == 1) {
|
||||
where = "AND (value LIKE :valuePrefix ESCAPE '/') ";
|
||||
boundaryCalc = "1";
|
||||
} else {
|
||||
where = "";
|
||||
boundaryCalc = "1";
|
||||
}
|
||||
/* Three factors in the frecency calculation for an entry (in order of use in calculation):
|
||||
* 1) average number of times used - items used more are ranked higher
|
||||
* 2) how recently it was last used - items used recently are ranked higher
|
||||
* 3) additional weight for aged entries surviving expiry - these entries are relevant
|
||||
* since they have been used multiple times over a large time span so rank them higher
|
||||
* The score is then divided by the bucket size and we round the result so that entries
|
||||
* with a very similar frecency are bucketed together with an alphabetical sort. This is
|
||||
* to reduce the amount of moving around by entries while typing.
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
let query = "/* do not warn (bug 496471): can't use an index */ " +
|
||||
"SELECT value, " +
|
||||
"ROUND( " +
|
||||
"timesUsed / MAX(1.0, (lastUsed - firstUsed) / :timeGroupingSize) * " +
|
||||
"MAX(1.0, :maxTimeGroupings - (:now - lastUsed) / :timeGroupingSize) * "+
|
||||
"MAX(1.0, :agedWeight * (firstUsed < :expiryDate)) / " +
|
||||
":bucketSize "+
|
||||
", 3) AS frecency, " +
|
||||
boundaryCalc + " AS boundaryBonuses " +
|
||||
"FROM moz_formhistory " +
|
||||
"WHERE fieldname=:fieldname " + where +
|
||||
"ORDER BY ROUND(frecency * boundaryBonuses) DESC, UPPER(value) ASC";
|
||||
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this._dbCreateStatement(query, params);
|
||||
|
||||
// Chicken and egg problem: Need the statement to escape the params we
|
||||
// pass to the function that gives us the statement. So, fix it up now.
|
||||
if (searchString.length >= 1)
|
||||
stmt.params.valuePrefix = stmt.escapeStringForLIKE(searchString, "/") + "%";
|
||||
if (searchString.length > 1) {
|
||||
for (let i = 0; i < searchTokens.length; i++) {
|
||||
let escapedToken = stmt.escapeStringForLIKE(searchTokens[i], "/");
|
||||
stmt.params["tokenBegin" + i] = escapedToken + "%";
|
||||
stmt.params["tokenBoundary" + i] = "% " + escapedToken + "%";
|
||||
stmt.params["tokenContains" + i] = "%" + escapedToken + "%";
|
||||
}
|
||||
} else {
|
||||
// no addional params need to be substituted into the query when the
|
||||
// length is zero or one
|
||||
}
|
||||
|
||||
while (stmt.executeStep()) {
|
||||
let entry = {
|
||||
text: stmt.row.value,
|
||||
textLowerCase: stmt.row.value.toLowerCase(),
|
||||
frecency: stmt.row.frecency,
|
||||
totalScore: Math.round(stmt.row.frecency * stmt.row.boundaryBonuses)
|
||||
}
|
||||
values.push(entry);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this.log("getValues failed: " + e.name + " : " + e.message);
|
||||
throw "DB failed getting form autocomplete values";
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
|
||||
_dbStmts : null,
|
||||
|
||||
_dbCreateStatement : function (query, params) {
|
||||
let stmt = this._dbStmts[query];
|
||||
// Memoize the statements
|
||||
if (!stmt) {
|
||||
this.log("Creating new statement for query: " + query);
|
||||
stmt = this._formHistory.DBConnection.createStatement(query);
|
||||
this._dbStmts[query] = stmt;
|
||||
}
|
||||
// Replace parameters, must be done 1 at a time
|
||||
if (params) {
|
||||
let stmtparams = stmt.params;
|
||||
for (let i in params)
|
||||
stmtparams[i] = params[i];
|
||||
}
|
||||
return stmt;
|
||||
this._pendingQuery = FormHistory.getAutoCompleteResults(searchString, params, processResults);
|
||||
},
|
||||
|
||||
/*
|
||||
@ -422,8 +382,11 @@ FormAutoCompleteResult.prototype = {
|
||||
|
||||
let [removedEntry] = this.entries.splice(index, 1);
|
||||
|
||||
if (removeFromDB)
|
||||
this.formHistory.removeEntry(this.fieldName, removedEntry.text);
|
||||
if (removeFromDB) {
|
||||
this.formHistory.update({ op: "remove",
|
||||
fieldname: this.fieldName,
|
||||
value: removedEntry.text });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -38,11 +38,12 @@
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_ISUPPORTS5(nsFormFillController,
|
||||
NS_IMPL_ISUPPORTS6(nsFormFillController,
|
||||
nsIFormFillController,
|
||||
nsIAutoCompleteInput,
|
||||
nsIAutoCompleteSearch,
|
||||
nsIDOMEventListener,
|
||||
nsIFormAutoCompleteObserver,
|
||||
nsIMutationObserver)
|
||||
|
||||
nsFormFillController::nsFormFillController() :
|
||||
@ -602,11 +603,15 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
|
||||
// XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
|
||||
// satchel manage the field?
|
||||
rv = mLoginManager->AutoCompleteSearch(aSearchString,
|
||||
aPreviousResult,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
aPreviousResult,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aListener) {
|
||||
aListener->OnSearchResult(this, result);
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIAutoCompleteResult> formHistoryResult;
|
||||
mLastListener = aListener;
|
||||
|
||||
// It appears that mFocusedInput is always null when we are focusing a XUL
|
||||
// element. Scary :)
|
||||
@ -615,48 +620,65 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
|
||||
do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = formAutoComplete->AutoCompleteSearch(aSearchParam,
|
||||
formAutoComplete->AutoCompleteSearchAsync(aSearchParam,
|
||||
aSearchString,
|
||||
mFocusedInput,
|
||||
aPreviousResult,
|
||||
getter_AddRefs(formHistoryResult));
|
||||
this);
|
||||
mLastFormAutoComplete = formAutoComplete;
|
||||
} else {
|
||||
mLastSearchString = aSearchString;
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Even if autocomplete is disabled, handle the inputlist anyway as that was
|
||||
// specifically requested by the page. This is so a field can have the default
|
||||
// autocomplete disabled and replaced with a custom inputlist autocomplete.
|
||||
return PerformInputListAutoComplete(aPreviousResult);
|
||||
}
|
||||
}
|
||||
|
||||
mLastSearchResult = formHistoryResult;
|
||||
mLastListener = aListener;
|
||||
mLastSearchString = aSearchString;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsresult
|
||||
nsFormFillController::PerformInputListAutoComplete(nsIAutoCompleteResult* aPreviousResult)
|
||||
{
|
||||
// If an <input> is focused, check if it has a list="<datalist>" which can
|
||||
// provide the list of suggestions.
|
||||
|
||||
rv = inputListAutoComplete->AutoCompleteSearch(formHistoryResult,
|
||||
aSearchString,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIAutoCompleteResult> result;
|
||||
|
||||
if (mFocusedInput) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> list;
|
||||
mFocusedInput->GetList(getter_AddRefs(list));
|
||||
nsCOMPtr <nsIInputListAutoComplete> inputListAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/inputlist-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = inputListAutoComplete->AutoCompleteSearch(aPreviousResult,
|
||||
mLastSearchString,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(list);
|
||||
if (mListNode != node) {
|
||||
if (mListNode) {
|
||||
mListNode->RemoveMutationObserver(this);
|
||||
mListNode = nullptr;
|
||||
}
|
||||
if (node) {
|
||||
node->AddMutationObserverUnlessExists(this);
|
||||
mListNode = node;
|
||||
}
|
||||
if (mFocusedInput) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> list;
|
||||
mFocusedInput->GetList(getter_AddRefs(list));
|
||||
|
||||
// Add a mutation observer to check for changes to the items in the <datalist>
|
||||
// and update the suggestions accordingly.
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(list);
|
||||
if (mListNode != node) {
|
||||
if (mListNode) {
|
||||
mListNode->RemoveMutationObserver(this);
|
||||
mListNode = nullptr;
|
||||
}
|
||||
if (node) {
|
||||
node->AddMutationObserverUnlessExists(this);
|
||||
mListNode = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aListener->OnSearchResult(this, result);
|
||||
if (mLastListener) {
|
||||
mLastListener->OnSearchResult(this, result);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -709,9 +731,31 @@ void nsFormFillController::RevalidateDataList()
|
||||
NS_IMETHODIMP
|
||||
nsFormFillController::StopSearch()
|
||||
{
|
||||
// Make sure to stop and clear this, otherwise the controller will prevent
|
||||
// mLastFormAutoComplete from being deleted.
|
||||
if (mLastFormAutoComplete) {
|
||||
mLastFormAutoComplete->StopAutoCompleteSearch();
|
||||
mLastFormAutoComplete = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//// nsIFormAutoCompleteObserver
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFormFillController::OnSearchCompletion(nsIAutoCompleteResult *aResult)
|
||||
{
|
||||
nsCOMPtr<nsIAutoCompleteResult> resultParam = do_QueryInterface(aResult);
|
||||
|
||||
nsAutoString searchString;
|
||||
resultParam->GetSearchString(searchString);
|
||||
mLastSearchResult = aResult;
|
||||
mLastSearchString = searchString;
|
||||
|
||||
return PerformInputListAutoComplete(resultParam);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//// nsIDOMEventListener
|
||||
|
||||
@ -1167,4 +1211,3 @@ static const mozilla::Module kSatchelModule = {
|
||||
};
|
||||
|
||||
NSMODULE_DEFN(satchel) = &kSatchelModule;
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsIAutoCompleteSearch.h"
|
||||
#include "nsIAutoCompleteController.h"
|
||||
#include "nsIAutoCompletePopup.h"
|
||||
#include "nsIFormAutoComplete.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
@ -33,6 +34,7 @@ class nsFormFillController : public nsIFormFillController,
|
||||
public nsIAutoCompleteInput,
|
||||
public nsIAutoCompleteSearch,
|
||||
public nsIDOMEventListener,
|
||||
public nsIFormAutoCompleteObserver,
|
||||
public nsIMutationObserver
|
||||
{
|
||||
public:
|
||||
@ -40,6 +42,7 @@ public:
|
||||
NS_DECL_NSIFORMFILLCONTROLLER
|
||||
NS_DECL_NSIAUTOCOMPLETESEARCH
|
||||
NS_DECL_NSIAUTOCOMPLETEINPUT
|
||||
NS_DECL_NSIFORMAUTOCOMPLETEOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
NS_DECL_NSIMUTATIONOBSERVER
|
||||
|
||||
@ -60,6 +63,8 @@ protected:
|
||||
void StartControllingInput(nsIDOMHTMLInputElement *aInput);
|
||||
void StopControllingInput();
|
||||
|
||||
nsresult PerformInputListAutoComplete(nsIAutoCompleteResult* aPreviousResult);
|
||||
|
||||
void RevalidateDataList();
|
||||
bool RowMatch(nsFormHistory *aHistory, uint32_t aIndex, const nsAString &aInputName, const nsAString &aInputValue);
|
||||
|
||||
@ -79,6 +84,9 @@ protected:
|
||||
nsCOMPtr<nsILoginManager> mLoginManager;
|
||||
nsIDOMHTMLInputElement* mFocusedInput;
|
||||
nsINode* mFocusedInputNode;
|
||||
|
||||
// mListNode is a <datalist> element which, is set, has the form fill controller
|
||||
// as a mutation observer for it.
|
||||
nsINode* mListNode;
|
||||
nsCOMPtr<nsIAutoCompletePopup> mFocusedPopup;
|
||||
|
||||
@ -87,7 +95,13 @@ protected:
|
||||
|
||||
//these are used to dynamically update the autocomplete
|
||||
nsCOMPtr<nsIAutoCompleteResult> mLastSearchResult;
|
||||
|
||||
// The observer passed to StartSearch. It will be notified when the search is
|
||||
// complete or the data from a datalist changes.
|
||||
nsCOMPtr<nsIAutoCompleteObserver> mLastListener;
|
||||
|
||||
// This is cleared by StopSearch().
|
||||
nsCOMPtr<nsIFormAutoComplete> mLastFormAutoComplete;
|
||||
nsString mLastSearchString;
|
||||
|
||||
nsDataHashtable<nsPtrHashKey<const nsINode>, bool> mPwmgrInputs;
|
||||
|
@ -27,7 +27,6 @@ FormHistory.prototype = {
|
||||
|
||||
debug : true,
|
||||
enabled : true,
|
||||
saveHttpsForms : true,
|
||||
|
||||
// The current database schema.
|
||||
dbSchema : {
|
||||
@ -82,43 +81,14 @@ FormHistory.prototype = {
|
||||
|
||||
|
||||
init : function init() {
|
||||
Services.prefs.addObserver("browser.formfill.", this, true);
|
||||
|
||||
this.updatePrefs();
|
||||
|
||||
this.dbStmts = {};
|
||||
|
||||
this.messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
|
||||
getService(Ci.nsIMessageListenerManager);
|
||||
this.messageManager.loadFrameScript("chrome://satchel/content/formSubmitListener.js", true);
|
||||
this.messageManager.addMessageListener("FormHistory:FormSubmitEntries", this);
|
||||
|
||||
// Add observers
|
||||
// Add observer
|
||||
Services.obs.addObserver(this, "profile-before-change", true);
|
||||
Services.obs.addObserver(this, "idle-daily", true);
|
||||
Services.obs.addObserver(this, "formhistory-expire-now", true);
|
||||
},
|
||||
|
||||
/* ---- message listener ---- */
|
||||
|
||||
|
||||
receiveMessage: function receiveMessage(message) {
|
||||
// Open a transaction so multiple adds happen in one commit
|
||||
this.dbConnection.beginTransaction();
|
||||
|
||||
try {
|
||||
let entries = message.json;
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
this.addEntry(entries[i].name, entries[i].value);
|
||||
}
|
||||
} finally {
|
||||
// Don't need it to be atomic if there was an error. Commit what
|
||||
// we managed to put in the table.
|
||||
this.dbConnection.commitTransaction();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* ---- nsIFormHistory2 interfaces ---- */
|
||||
|
||||
|
||||
@ -434,10 +404,6 @@ FormHistory.prototype = {
|
||||
case "nsPref:changed":
|
||||
this.updatePrefs();
|
||||
break;
|
||||
case "idle-daily":
|
||||
case "formhistory-expire-now":
|
||||
this.expireOldEntries();
|
||||
break;
|
||||
case "profile-before-change":
|
||||
this._dbClose(false);
|
||||
break;
|
||||
@ -569,56 +535,9 @@ FormHistory.prototype = {
|
||||
},
|
||||
|
||||
|
||||
expireOldEntries : function () {
|
||||
this.log("expireOldEntries");
|
||||
|
||||
// Determine how many days of history we're supposed to keep.
|
||||
let expireDays = 180;
|
||||
try {
|
||||
expireDays = Services.prefs.getIntPref("browser.formfill.expire_days");
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
let expireTime = Date.now() - expireDays * DAY_IN_MS;
|
||||
expireTime *= 1000; // switch to microseconds
|
||||
|
||||
this.sendIntNotification("before-expireOldEntries", expireTime);
|
||||
|
||||
let beginningCount = this.countAllEntries();
|
||||
|
||||
// Purge the form history...
|
||||
let stmt;
|
||||
let query = "DELETE FROM moz_formhistory WHERE lastUsed <= :expireTime";
|
||||
let params = { expireTime : expireTime };
|
||||
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
} catch (e) {
|
||||
this.log("expireOldEntries failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
let endingCount = this.countAllEntries();
|
||||
|
||||
// If we expired a large batch of entries, shrink the DB to reclaim wasted
|
||||
// space. This is expected to happen when entries predating timestamps
|
||||
// (added in the v.1 schema) expire in mass, 180 days after the DB was
|
||||
// upgraded -- entries not used since then expire all at once.
|
||||
if (beginningCount - endingCount > 500)
|
||||
this.dbConnection.executeSimpleSQL("VACUUM");
|
||||
|
||||
this.sendIntNotification("expireOldEntries", expireTime);
|
||||
},
|
||||
|
||||
|
||||
updatePrefs : function () {
|
||||
this.debug = Services.prefs.getBoolPref("browser.formfill.debug");
|
||||
this.enabled = Services.prefs.getBoolPref("browser.formfill.enable");
|
||||
this.saveHttpsForms = Services.prefs.getBoolPref("browser.formfill.saveHttpsForms");
|
||||
},
|
||||
|
||||
//**************************************************************************//
|
||||
|
@ -6,17 +6,47 @@
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIAutoCompleteResult;
|
||||
interface nsIFormAutoCompleteObserver;
|
||||
interface nsIDOMHTMLInputElement;
|
||||
|
||||
[scriptable, uuid(997c0c05-5d1d-47e5-9cbc-765c0b8ec699)]
|
||||
[scriptable, uuid(c079f18f-40ab-409d-800e-878889b83b58)]
|
||||
|
||||
interface nsIFormAutoComplete: nsISupports {
|
||||
|
||||
/**
|
||||
* Generate results for a form input autocomplete menu.
|
||||
* Generate results for a form input autocomplete menu synchronously.
|
||||
* This method is deprecated in favour of autoCompleteSearchAsync.
|
||||
*/
|
||||
nsIAutoCompleteResult autoCompleteSearch(
|
||||
in AString aInputName,
|
||||
in AString aSearchString,
|
||||
in nsIDOMHTMLInputElement aField,
|
||||
in nsIAutoCompleteResult aPreviousResult);
|
||||
nsIAutoCompleteResult autoCompleteSearch(in AString aInputName,
|
||||
in AString aSearchString,
|
||||
in nsIDOMHTMLInputElement aField,
|
||||
in nsIAutoCompleteResult aPreviousResult);
|
||||
|
||||
/**
|
||||
* Generate results for a form input autocomplete menu asynchronously.
|
||||
*/
|
||||
void autoCompleteSearchAsync(in AString aInputName,
|
||||
in AString aSearchString,
|
||||
in nsIDOMHTMLInputElement aField,
|
||||
in nsIAutoCompleteResult aPreviousResult,
|
||||
in nsIFormAutoCompleteObserver aListener);
|
||||
|
||||
/**
|
||||
* If a search is in progress, stop it. Otherwise, do nothing. This is used
|
||||
* to cancel an existing search, for example, in preparation for a new search.
|
||||
*/
|
||||
void stopAutoCompleteSearch();
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(604419ab-55a0-4831-9eca-1b9e67cc4751)]
|
||||
interface nsIFormAutoCompleteObserver : nsISupports
|
||||
{
|
||||
/*
|
||||
* Called when a search is complete and the results are ready even if the
|
||||
* result set is empty. If the search is cancelled or a new search is
|
||||
* started, this is not called.
|
||||
*
|
||||
* @param result - The search result object
|
||||
*/
|
||||
void onSearchCompletion(in nsIAutoCompleteResult result);
|
||||
};
|
||||
|
@ -4,3 +4,7 @@ component {c11c21b2-71c9-4f87-a0f8-5e13f50495fd} nsFormAutoComplete.js
|
||||
contract @mozilla.org/satchel/form-autocomplete;1 {c11c21b2-71c9-4f87-a0f8-5e13f50495fd}
|
||||
component {bf1e01d0-953e-11df-981c-0800200c9a66} nsInputListAutoComplete.js
|
||||
contract @mozilla.org/satchel/inputlist-autocomplete;1 {bf1e01d0-953e-11df-981c-0800200c9a66}
|
||||
component {3a0012eb-007f-4bb8-aa81-a07385f77a25} FormHistoryStartup.js
|
||||
contract @mozilla.org/satchel/form-history-startup;1 {3a0012eb-007f-4bb8-aa81-a07385f77a25}
|
||||
category profile-after-change formHistoryStartup @mozilla.org/satchel/form-history-startup;1
|
||||
category idle-daily formHistoryStartup @mozilla.org/satchel/form-history-startup;1
|
||||
|
@ -2,6 +2,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
|
||||
|
||||
/** Test for Bug 472396 **/
|
||||
function test() {
|
||||
// initialization
|
||||
@ -9,23 +11,40 @@ function test() {
|
||||
let windowsToClose = [];
|
||||
let testURI =
|
||||
"http://example.com/tests/toolkit/components/satchel/test/subtst_privbrowsing.html";
|
||||
let formHistory = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
function doTest(aIsPrivateMode, aShouldValueExist, aWindow, aCallback) {
|
||||
aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
|
||||
let checks = 0;
|
||||
function doneCheck() {
|
||||
checks++;
|
||||
if (checks == 2) {
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the second load of the page to call the callback,
|
||||
// because the first load submits the form and the page reloads after
|
||||
// the form submission.
|
||||
aWindow.gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
aWindow.gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(aCallback);
|
||||
doneCheck();
|
||||
}, true);
|
||||
|
||||
is(formHistory.entryExists("field", "value"), aShouldValueExist,
|
||||
"Checking value exists in form history");
|
||||
let count = 0;
|
||||
FormHistory.count({ fieldname: "field", value: "value" },
|
||||
{ handleResult: function(result) {
|
||||
count = result;
|
||||
},
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
},
|
||||
handleCompletion: function(num) {
|
||||
is(count >= 1, aShouldValueExist, "Checking value exists in form history");
|
||||
doneCheck();
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
aWindow.gBrowser.selectedBrowser.loadURI(testURI);
|
||||
|
@ -71,9 +71,7 @@ function getAutocompletePopup() {
|
||||
|
||||
|
||||
function cleanUpFormHist() {
|
||||
var formhist = SpecialPowers.Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(SpecialPowers.Ci.nsIFormHistory2);
|
||||
formhist.removeAllEntries();
|
||||
SpecialPowers.formHistory.update({ op : "remove" });
|
||||
}
|
||||
cleanUpFormHist();
|
||||
|
||||
@ -90,7 +88,7 @@ var checkObserver = {
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
if (data != "addEntry" && data != "modifyEntry")
|
||||
if (data != "formhistory-add" && data != "formhistory-update")
|
||||
return;
|
||||
ok(this.verifyStack.length > 0, "checking if saved form data was expected");
|
||||
|
||||
@ -104,13 +102,16 @@ var checkObserver = {
|
||||
// - if there are too many messages, test will error out here
|
||||
//
|
||||
var expected = this.verifyStack.shift();
|
||||
ok(fh.entryExists(expected.name, expected.value), expected.message);
|
||||
|
||||
if (this.verifyStack.length == 0) {
|
||||
var callback = this.callback;
|
||||
this.callback = null;
|
||||
callback();
|
||||
}
|
||||
countEntries(expected.name, expected.value,
|
||||
function(num) {
|
||||
ok(num > 0, expected.message);
|
||||
if (checkObserver.verifyStack.length == 0) {
|
||||
var callback = checkObserver.callback;
|
||||
checkObserver.callback = null;
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -131,3 +132,30 @@ function getFormSubmitButton(formNum) {
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
// Count the number of entries with the given name and value, and call then(number)
|
||||
// when done. If name or value is null, then the value of that field does not matter.
|
||||
function countEntries(name, value, then) {
|
||||
var obj = {};
|
||||
if (name !== null)
|
||||
obj.fieldname = name;
|
||||
if (value !== null)
|
||||
obj.value = value;
|
||||
|
||||
var count = 0;
|
||||
SpecialPowers.formHistory.count(obj, { handleResult: function (result) { count = result },
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) then(count); }
|
||||
});
|
||||
}
|
||||
|
||||
// Wrapper around FormHistory.update which handles errors. Calls then() when done.
|
||||
function updateFormHistory(changes, then) {
|
||||
SpecialPowers.formHistory.update(changes, { handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) then(); },
|
||||
});
|
||||
}
|
||||
|
@ -36,20 +36,20 @@ autocompletePopup.style.direction = "ltr";
|
||||
var input = $_(1, "field1");
|
||||
|
||||
// Get the form history service
|
||||
var fh = SpecialPowers.Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(SpecialPowers.Ci.nsIFormHistory2);
|
||||
ok(fh != null, "got form history service");
|
||||
|
||||
fh.removeAllEntries();
|
||||
fh.addEntry("field1", "value1");
|
||||
fh.addEntry("field1", "value2");
|
||||
fh.addEntry("field1", "value3");
|
||||
fh.addEntry("field1", "value4");
|
||||
fh.addEntry("field1", "value5");
|
||||
fh.addEntry("field1", "value6");
|
||||
fh.addEntry("field1", "value7");
|
||||
fh.addEntry("field1", "value8");
|
||||
fh.addEntry("field1", "value9");
|
||||
function setupFormHistory(aCallback) {
|
||||
updateFormHistory([
|
||||
{ op : "remove" },
|
||||
{ op : "add", fieldname : "field1", value : "value1" },
|
||||
{ op : "add", fieldname : "field1", value : "value2" },
|
||||
{ op : "add", fieldname : "field1", value : "value3" },
|
||||
{ op : "add", fieldname : "field1", value : "value4" },
|
||||
{ op : "add", fieldname : "field1", value : "value5" },
|
||||
{ op : "add", fieldname : "field1", value : "value6" },
|
||||
{ op : "add", fieldname : "field1", value : "value7" },
|
||||
{ op : "add", fieldname : "field1", value : "value8" },
|
||||
{ op : "add", fieldname : "field1", value : "value9" },
|
||||
], aCallback);
|
||||
}
|
||||
|
||||
function checkForm(expectedValue) {
|
||||
var formID = input.parentNode.id;
|
||||
@ -117,6 +117,29 @@ function doClickUnprivileged() {
|
||||
input.dispatchEvent(ckEvent);
|
||||
}
|
||||
|
||||
var testNum = 0;
|
||||
var expectingPopup = false;
|
||||
|
||||
function expectPopup()
|
||||
{
|
||||
info("expecting popup for test " + testNum);
|
||||
expectingPopup = true;
|
||||
}
|
||||
|
||||
function popupShownListener()
|
||||
{
|
||||
info("popup shown for test " + testNum);
|
||||
if (expectingPopup) {
|
||||
expectingPopup = false;
|
||||
SimpleTest.executeSoon(runTest);
|
||||
}
|
||||
else {
|
||||
ok(false, "Autocomplete popup not expected" + testNum);
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.addAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
|
||||
/*
|
||||
* Main section of test...
|
||||
*
|
||||
@ -125,7 +148,9 @@ function doClickUnprivileged() {
|
||||
* setTimeout() calls. The case statements are executed in order, one per
|
||||
* timeout.
|
||||
*/
|
||||
function runTest(testNum) {
|
||||
function runTest() {
|
||||
testNum++;
|
||||
|
||||
ok(true, "Starting test #" + testNum);
|
||||
|
||||
switch(testNum) {
|
||||
@ -218,7 +243,9 @@ function runTest(testNum) {
|
||||
// We're privileged for this test, so open the popup.
|
||||
checkPopupOpen(false);
|
||||
checkForm("");
|
||||
expectPopup();
|
||||
doKey("down");
|
||||
return;
|
||||
break;
|
||||
case 21:
|
||||
checkPopupOpen(true, -1);
|
||||
@ -336,20 +363,22 @@ function runTest(testNum) {
|
||||
checkForm("");
|
||||
is(autocompletePopup.style.direction, "rtl", "direction should have been changed from ltr to rtl");
|
||||
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(runTest, 50, testNum + 1);
|
||||
SimpleTest.executeSoon(runTest);
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
runTest(1);
|
||||
setupFormHistory(runTest);
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
|
@ -65,13 +65,13 @@ var input = $_(1, "field1");
|
||||
var rect = input.getBoundingClientRect();
|
||||
|
||||
// Get the form history service
|
||||
var fh = SpecialPowers.Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(SpecialPowers.Ci.nsIFormHistory2);
|
||||
ok(fh != null, "got form history service");
|
||||
|
||||
fh.removeAllEntries();
|
||||
fh.addEntry("field1", "value1");
|
||||
fh.addEntry("field1", "value2");
|
||||
function setupFormHistory() {
|
||||
updateFormHistory([
|
||||
{ op : "remove" },
|
||||
{ op : "add", fieldname : "field1", value : "value1" },
|
||||
{ op : "add", fieldname : "field1", value : "value2" },
|
||||
], function() runTest(1));
|
||||
}
|
||||
|
||||
function checkForm(expectedValue) {
|
||||
var formID = input.parentNode.id;
|
||||
@ -172,7 +172,7 @@ function runTest(testNum) {
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
runTest(1);
|
||||
setupFormHistory();
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
|
@ -11,8 +11,9 @@
|
||||
Form History test: form field autocomplete
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
<!-- We presumably can't hide the content for this test. The large top padding is to allow
|
||||
listening for scrolls to occur. -->
|
||||
<div id="content" style="padding-top: 20000px;">
|
||||
|
||||
<!-- normal, basic form -->
|
||||
<form id="form1" onsubmit="return false;">
|
||||
@ -120,40 +121,39 @@ Form History test: form field autocomplete
|
||||
var input = $_(1, "field1");
|
||||
const shiftModifier = Event.SHIFT_MASK;
|
||||
|
||||
// Get the form history service
|
||||
var fh = SpecialPowers.Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(SpecialPowers.Ci.nsIFormHistory2);
|
||||
ok(fh != null, "got form history service");
|
||||
|
||||
fh.removeAllEntries();
|
||||
fh.addEntry("field1", "value1");
|
||||
fh.addEntry("field1", "value2");
|
||||
fh.addEntry("field1", "value3");
|
||||
fh.addEntry("field1", "value4");
|
||||
fh.addEntry("field2", "value1");
|
||||
fh.addEntry("field3", "a");
|
||||
fh.addEntry("field3", "aa");
|
||||
fh.addEntry("field3", "aaz");
|
||||
fh.addEntry("field3", "aa\xe6"); // 0xae == latin ae pair (0xc6 == AE)
|
||||
fh.addEntry("field3", "az");
|
||||
fh.addEntry("field3", "z");
|
||||
fh.addEntry("field4", "a\xe6");
|
||||
fh.addEntry("field4", "aa a\xe6");
|
||||
fh.addEntry("field4", "aba\xe6");
|
||||
fh.addEntry("field4", "bc d\xe6");
|
||||
fh.addEntry("field5", "1");
|
||||
fh.addEntry("field5", "12");
|
||||
fh.addEntry("field5", "123");
|
||||
fh.addEntry("field5", "1234");
|
||||
fh.addEntry("field6", "value");
|
||||
fh.addEntry("field7", "value");
|
||||
fh.addEntry("field8", "value");
|
||||
fh.addEntry("field9", "value");
|
||||
fh.addEntry("field10", "42");
|
||||
fh.addEntry("field11", "2010-10-10");
|
||||
fh.addEntry("field12", "21:21");
|
||||
fh.addEntry("field13", "32"); // not used, since type=range doesn't have a drop down menu
|
||||
fh.addEntry("searchbar-history", "blacklist test");
|
||||
function setupFormHistory(aCallback) {
|
||||
updateFormHistory([
|
||||
{ op : "remove" },
|
||||
{ op : "add", fieldname : "field1", value : "value1" },
|
||||
{ op : "add", fieldname : "field1", value : "value2" },
|
||||
{ op : "add", fieldname : "field1", value : "value3" },
|
||||
{ op : "add", fieldname : "field1", value : "value4" },
|
||||
{ op : "add", fieldname : "field2", value : "value1" },
|
||||
{ op : "add", fieldname : "field3", value : "a" },
|
||||
{ op : "add", fieldname : "field3", value : "aa" },
|
||||
{ op : "add", fieldname : "field3", value : "aaz" },
|
||||
{ op : "add", fieldname : "field3", value : "aa\xe6" }, // 0xae == latin ae pair (0xc6 == AE)
|
||||
{ op : "add", fieldname : "field3", value : "az" },
|
||||
{ op : "add", fieldname : "field3", value : "z" },
|
||||
{ op : "add", fieldname : "field4", value : "a\xe6" },
|
||||
{ op : "add", fieldname : "field4", value : "aa a\xe6" },
|
||||
{ op : "add", fieldname : "field4", value : "aba\xe6" },
|
||||
{ op : "add", fieldname : "field4", value : "bc d\xe6" },
|
||||
{ op : "add", fieldname : "field5", value : "1" },
|
||||
{ op : "add", fieldname : "field5", value : "12" },
|
||||
{ op : "add", fieldname : "field5", value : "123" },
|
||||
{ op : "add", fieldname : "field5", value : "1234" },
|
||||
{ op : "add", fieldname : "field6", value : "value" },
|
||||
{ op : "add", fieldname : "field7", value : "value" },
|
||||
{ op : "add", fieldname : "field8", value : "value" },
|
||||
{ op : "add", fieldname : "field9", value : "value" },
|
||||
{ op : "add", fieldname : "field10", value : "42" },
|
||||
{ op : "add", fieldname : "field11", value : "2010-10-10" },
|
||||
{ op : "add", fieldname : "field12", value : "21:21" },
|
||||
{ op : "add", fieldname : "field13", value : "32" }, // not used, since type=range doesn't have a drop down menu
|
||||
{ op : "add", fieldname : "searchbar-history", value : "blacklist test" },
|
||||
], aCallback);
|
||||
}
|
||||
|
||||
// All these non-implemeted types might need autocomplete tests in the future.
|
||||
var todoTypes = [ "datetime", "month", "week", "datetime-local", "color" ];
|
||||
@ -182,16 +182,45 @@ function checkForm(expectedValue) {
|
||||
is(input.value, expectedValue, "Checking " + formID + " input");
|
||||
}
|
||||
|
||||
var testNum = 0;
|
||||
var expectingPopup = false;
|
||||
|
||||
function expectPopup()
|
||||
{
|
||||
info("expecting popup for test " + testNum);
|
||||
expectingPopup = true;
|
||||
}
|
||||
|
||||
function popupShownListener()
|
||||
{
|
||||
info("popup shown for test " + testNum);
|
||||
if (expectingPopup) {
|
||||
expectingPopup = false;
|
||||
SimpleTest.executeSoon(runTest);
|
||||
}
|
||||
else {
|
||||
ok(false, "Autocomplete popup not expected during test " + testNum);
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.addAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
|
||||
/*
|
||||
* Main section of test...
|
||||
*
|
||||
* This is a bit hacky, because the events are either being sent or
|
||||
* processed asynchronously, so we need to interrupt our flow with lots of
|
||||
* setTimeout() calls. The case statements are executed in order, one per
|
||||
* timeout.
|
||||
* This is a bit hacky, as many operations happen asynchronously.
|
||||
* Various mechanisms call runTests as a result of operations:
|
||||
* - set expectingPopup to true, and the next test will occur when the autocomplete popup is shown
|
||||
* - call waitForMenuChange(x) to run the next test when the autocomplete popup to have x items in it
|
||||
* - addEntry calls runs the test when an entry has been added
|
||||
* - some tests scroll the window. This is because the form fill controller happens to scroll
|
||||
* the field into view near the end of the search, and there isn't any other good notification
|
||||
* to listen to for when the search is complete.
|
||||
* - some items still use setTimeout
|
||||
*/
|
||||
function runTest(testNum) {
|
||||
function runTest() {
|
||||
testNum++;
|
||||
|
||||
ok(true, "Starting test #" + testNum);
|
||||
|
||||
switch(testNum) {
|
||||
@ -199,12 +228,13 @@ function runTest(testNum) {
|
||||
// Make sure initial form is empty.
|
||||
checkForm("");
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"]);
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"], testNum);
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkForm(""); // value shouldn't update
|
||||
@ -212,6 +242,7 @@ function runTest(testNum) {
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -224,6 +255,7 @@ function runTest(testNum) {
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -237,6 +269,7 @@ function runTest(testNum) {
|
||||
checkForm("value3");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -251,6 +284,7 @@ function runTest(testNum) {
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -267,6 +301,7 @@ function runTest(testNum) {
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -278,6 +313,7 @@ function runTest(testNum) {
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -291,6 +327,7 @@ function runTest(testNum) {
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -309,6 +346,7 @@ function runTest(testNum) {
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -320,6 +358,7 @@ function runTest(testNum) {
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -331,6 +370,7 @@ function runTest(testNum) {
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -344,6 +384,7 @@ function runTest(testNum) {
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -356,15 +397,16 @@ function runTest(testNum) {
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
testNum = 49;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 49;
|
||||
break;
|
||||
|
||||
/* Test removing entries from the dropdown */
|
||||
|
||||
case 50:
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"]);
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"], testNum);
|
||||
// Delete the first entry (of 4)
|
||||
setForm("value");
|
||||
doKey("down");
|
||||
@ -378,95 +420,144 @@ function runTest(testNum) {
|
||||
|
||||
// This tests that on OS X shift-backspace didn't delete the last character
|
||||
// in the input (bug 480262).
|
||||
checkForm("value");
|
||||
waitForMenuChange(3);
|
||||
break;
|
||||
|
||||
ok(!fh.entryExists("field1", "value1"), "checking that f1/v1 was deleted");
|
||||
case 51:
|
||||
checkForm("value");
|
||||
countEntries("field1", "value1",
|
||||
function (num) {
|
||||
ok(!num, testNum + " checking that f1/v1 was deleted");
|
||||
runTest();
|
||||
});
|
||||
break;
|
||||
|
||||
case 52:
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 51:
|
||||
checkMenuEntries(["value2", "value3", "value4"]);
|
||||
case 53:
|
||||
checkMenuEntries(["value2", "value3", "value4"], testNum);
|
||||
// Check the new first entry (of 3)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 52:
|
||||
case 54:
|
||||
// Delete the second entry (of 3)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
waitForMenuChange(2);
|
||||
break;
|
||||
|
||||
case 55:
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value3"), "checking that f1/v3 was deleted");
|
||||
countEntries("field1", "value3",
|
||||
function (num) {
|
||||
ok(!num, testNum + " checking that f1/v3 was deleted");
|
||||
runTest();
|
||||
});
|
||||
break;
|
||||
|
||||
case 56:
|
||||
doKey("return");
|
||||
checkForm("value4")
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 53:
|
||||
checkMenuEntries(["value2", "value4"]);
|
||||
case 57:
|
||||
checkMenuEntries(["value2", "value4"], testNum);
|
||||
// Check the new first entry (of 2)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 54:
|
||||
case 58:
|
||||
// Delete the last entry (of 2)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value4"), "checking that f1/v4 was deleted");
|
||||
waitForMenuChange(1);
|
||||
break;
|
||||
|
||||
case 59:
|
||||
countEntries("field1", "value4",
|
||||
function (num) {
|
||||
ok(!num, testNum + " checking that f1/v4 was deleted");
|
||||
runTest();
|
||||
});
|
||||
break;
|
||||
|
||||
case 60:
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 55:
|
||||
checkMenuEntries(["value2"]);
|
||||
case 61:
|
||||
checkMenuEntries(["value2"], testNum);
|
||||
// Check the new first entry (of 1)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 56:
|
||||
case 62:
|
||||
// Delete the only remaining entry
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value2"), "checking that f1/v2 was deleted");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 63:
|
||||
checkForm("");
|
||||
countEntries("field1", "value2",
|
||||
function (num) {
|
||||
ok(!num, testNum + " checking that f1/v2 was deleted");
|
||||
runTest();
|
||||
});
|
||||
break;
|
||||
|
||||
case 64:
|
||||
// Look at form 2, trigger autocomplete popup
|
||||
input = $_(2, "field2");
|
||||
testNum = 99;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 99;
|
||||
break;
|
||||
|
||||
/* Test entries with autocomplete=off */
|
||||
@ -480,7 +571,11 @@ function runTest(testNum) {
|
||||
// Look at form 3, try to trigger autocomplete popup
|
||||
input = $_(3, "field2");
|
||||
restoreForm();
|
||||
// Sometimes, this will fail if scrollTo(0, 0) is called, so that doesn't
|
||||
// happen here. Fortunately, a different input is used from the last test,
|
||||
// so a scroll should still occur.
|
||||
doKey("down");
|
||||
waitForScroll();
|
||||
break;
|
||||
|
||||
case 101:
|
||||
@ -493,6 +588,7 @@ function runTest(testNum) {
|
||||
input = $_(4, "field2");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 102:
|
||||
@ -505,6 +601,7 @@ function runTest(testNum) {
|
||||
input = $_(5, "field3");
|
||||
restoreForm();
|
||||
testNum = 199;
|
||||
expectPopup();
|
||||
input.focus();
|
||||
sendChar("a");
|
||||
break;
|
||||
@ -512,65 +609,81 @@ function runTest(testNum) {
|
||||
/* Test filtering as characters are typed. */
|
||||
|
||||
case 200:
|
||||
checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"]);
|
||||
checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"], testNum);
|
||||
input.focus();
|
||||
sendChar("a");
|
||||
waitForMenuChange(3);
|
||||
break;
|
||||
|
||||
case 201:
|
||||
checkMenuEntries(["aa", "aaz", "aa\xe6"]);
|
||||
checkMenuEntries(["aa", "aaz", "aa\xe6"], testNum);
|
||||
input.focus();
|
||||
sendChar("\xc6");
|
||||
waitForMenuChange(1);
|
||||
break;
|
||||
|
||||
case 202:
|
||||
checkMenuEntries(["aa\xe6"]);
|
||||
checkMenuEntries(["aa\xe6"], testNum);
|
||||
doKey("back_space");
|
||||
waitForMenuChange(3);
|
||||
break;
|
||||
|
||||
case 203:
|
||||
checkMenuEntries(["aa", "aaz", "aa\xe6"]);
|
||||
checkMenuEntries(["aa", "aaz", "aa\xe6"], testNum);
|
||||
doKey("back_space");
|
||||
waitForMenuChange(5);
|
||||
break;
|
||||
|
||||
case 204:
|
||||
checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"]);
|
||||
checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"], testNum);
|
||||
input.focus();
|
||||
sendChar("z");
|
||||
waitForMenuChange(2);
|
||||
break;
|
||||
|
||||
case 205:
|
||||
ok(getMenuEntries().length > 0, "checking typing in middle of text");
|
||||
checkMenuEntries(["az", "aaz"], testNum);
|
||||
input.focus();
|
||||
doKey("left");
|
||||
expectPopup();
|
||||
sendChar("a");
|
||||
break;
|
||||
|
||||
case 206:
|
||||
checkMenuEntries(["aaz"]);
|
||||
fh.addEntry("field3", "aazq");
|
||||
input.focus();
|
||||
doKey("right");
|
||||
sendChar("q");
|
||||
checkMenuEntries(["aaz"], testNum);
|
||||
addEntry("field3", "aazq");
|
||||
break;
|
||||
|
||||
case 207:
|
||||
// check that results were cached
|
||||
checkMenuEntries([]);
|
||||
fh.addEntry("field3", "aazqq");
|
||||
input.focus();
|
||||
doKey("right");
|
||||
sendChar("q");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 208:
|
||||
// check that results were cached
|
||||
checkMenuEntries([], testNum);
|
||||
addEntry("field3", "aazqq");
|
||||
break;
|
||||
|
||||
case 209:
|
||||
input.focus();
|
||||
window.scrollTo(0, 0);
|
||||
sendChar("q");
|
||||
waitForScroll();
|
||||
break;
|
||||
|
||||
case 210:
|
||||
// check that empty results were cached - bug 496466
|
||||
checkMenuEntries([]);
|
||||
checkMenuEntries([], testNum);
|
||||
doKey("escape");
|
||||
|
||||
// Look at form 6, try to trigger autocomplete popup
|
||||
input = $_(6, "field4");
|
||||
restoreForm();
|
||||
testNum = 249;
|
||||
expectPopup();
|
||||
input.focus();
|
||||
sendChar("a");
|
||||
break;
|
||||
@ -579,138 +692,159 @@ function runTest(testNum) {
|
||||
|
||||
case 250:
|
||||
// alphabetical results for first character
|
||||
checkMenuEntries(["aa a\xe6", "aba\xe6", "a\xe6"]);
|
||||
checkMenuEntries(["aa a\xe6", "aba\xe6", "a\xe6"], testNum);
|
||||
input.focus();
|
||||
|
||||
// for this test, hide the popup first as it contains the same number of menu
|
||||
|
||||
sendChar("\xc6");
|
||||
waitForMenuChange(3, "a\xe6");
|
||||
break;
|
||||
|
||||
case 251:
|
||||
// prefix match comes first, then word boundary match
|
||||
// followed by substring match
|
||||
checkMenuEntries(["a\xe6", "aa a\xe6", "aba\xe6"]);
|
||||
checkMenuEntries(["a\xe6", "aa a\xe6", "aba\xe6"], testNum);
|
||||
|
||||
restoreForm();
|
||||
input.focus();
|
||||
sendChar("b");
|
||||
waitForMenuChange(1, "bc d\xe6");
|
||||
break;
|
||||
|
||||
case 252:
|
||||
checkMenuEntries(["bc d\xe6"]);
|
||||
checkMenuEntries(["bc d\xe6"], testNum);
|
||||
input.focus();
|
||||
sendChar(" ");
|
||||
setTimeout(runTest, 300);
|
||||
break;
|
||||
|
||||
case 253:
|
||||
// check that trailing space has no effect after single char.
|
||||
checkMenuEntries(["bc d\xe6"]);
|
||||
checkMenuEntries(["bc d\xe6"], testNum);
|
||||
input.focus();
|
||||
sendChar("\xc6");
|
||||
waitForMenuChange(2);
|
||||
break;
|
||||
|
||||
case 254:
|
||||
// check multi-word substring matches
|
||||
checkMenuEntries(["bc d\xe6", "aba\xe6"]);
|
||||
input.focus();
|
||||
expectPopup();
|
||||
doKey("left");
|
||||
sendChar("d");
|
||||
break;
|
||||
|
||||
case 255:
|
||||
// check inserting in multi-word searches
|
||||
checkMenuEntries(["bc d\xe6"]);
|
||||
checkMenuEntries(["bc d\xe6"], testNum);
|
||||
input.focus();
|
||||
sendChar("z");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 256:
|
||||
checkMenuEntries([]);
|
||||
checkMenuEntries([], testNum);
|
||||
|
||||
// Look at form 7, try to trigger autocomplete popup
|
||||
input = $_(7, "field5");
|
||||
testNum = 299;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 299;
|
||||
break;
|
||||
|
||||
case 300:
|
||||
checkMenuEntries(["1", "12", "123", "1234"]);
|
||||
checkMenuEntries(["1", "12", "123", "1234"], testNum);
|
||||
input.maxLength = 4;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 301:
|
||||
checkMenuEntries(["1", "12", "123", "1234"]);
|
||||
checkMenuEntries(["1", "12", "123", "1234"], testNum);
|
||||
input.maxLength = 3;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 302:
|
||||
checkMenuEntries(["1", "12", "123"]);
|
||||
checkMenuEntries(["1", "12", "123"], testNum);
|
||||
input.maxLength = 2;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 303:
|
||||
checkMenuEntries(["1", "12"]);
|
||||
checkMenuEntries(["1", "12"], testNum);
|
||||
input.maxLength = 1;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 304:
|
||||
checkMenuEntries(["1"]);
|
||||
checkMenuEntries(["1"], testNum);
|
||||
input.maxLength = 0;
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 305:
|
||||
checkMenuEntries([]);
|
||||
checkMenuEntries([], testNum);
|
||||
input.maxLength = 4;
|
||||
|
||||
// now again with a character typed
|
||||
input.focus();
|
||||
sendChar("1");
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 306:
|
||||
checkMenuEntries(["1", "12", "123", "1234"]);
|
||||
checkMenuEntries(["1", "12", "123", "1234"], testNum);
|
||||
input.maxLength = 3;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 307:
|
||||
checkMenuEntries(["1", "12", "123"]);
|
||||
checkMenuEntries(["1", "12", "123"], testNum);
|
||||
input.maxLength = 2;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 308:
|
||||
checkMenuEntries(["1", "12"]);
|
||||
checkMenuEntries(["1", "12"], testNum);
|
||||
input.maxLength = 1;
|
||||
expectPopup();
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 309:
|
||||
checkMenuEntries(["1"]);
|
||||
checkMenuEntries(["1"], testNum);
|
||||
input.maxLength = 0;
|
||||
doKey("escape");
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 310:
|
||||
checkMenuEntries([]);
|
||||
checkMenuEntries([], testNum);
|
||||
|
||||
input = $_(8, "field6");
|
||||
testNum = 399;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -719,7 +853,7 @@ function runTest(testNum) {
|
||||
case 401:
|
||||
case 402:
|
||||
case 403:
|
||||
checkMenuEntries(["value"]);
|
||||
checkMenuEntries(["value"], testNum);
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value");
|
||||
@ -734,18 +868,20 @@ function runTest(testNum) {
|
||||
input = $_(12, "field10");
|
||||
}
|
||||
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 404:
|
||||
checkMenuEntries(["42"]);
|
||||
checkMenuEntries(["42"], testNum);
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("42");
|
||||
|
||||
input = $_(14, "field11");
|
||||
restoreForm();
|
||||
expectPopup();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
@ -757,6 +893,7 @@ function runTest(testNum) {
|
||||
|
||||
input = $_(15, "field12");
|
||||
restoreForm();
|
||||
expectPopup();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
@ -769,19 +906,24 @@ function runTest(testNum) {
|
||||
input = $_(16, "field13");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 407:
|
||||
case 407:
|
||||
checkMenuEntries([]); // type=range does not have a drop down menu
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("30"); // default (midway between minimum (0) and maximum (64)) - step
|
||||
|
||||
// Go to test 500.
|
||||
fh.addEntry("field1", "value1");
|
||||
addEntry("field1", "value1");
|
||||
break;
|
||||
|
||||
case 408:
|
||||
input = $_(1, "field1");
|
||||
// Go to test 500.
|
||||
testNum = 499;
|
||||
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -790,9 +932,9 @@ function runTest(testNum) {
|
||||
case 500:
|
||||
input.addEventListener("input", function(event) {
|
||||
input.removeEventListener("input", arguments.callee, false);
|
||||
ok(true, "oninput should have been received");
|
||||
ok(event.bubbles, "input event should bubble");
|
||||
ok(event.cancelable, "input event should be cancelable");
|
||||
ok(true, testNum + " oninput should have been received");
|
||||
ok(event.bubbles, testNum + " input event should bubble");
|
||||
ok(event.cancelable, testNum + " input event should be cancelable");
|
||||
}, false);
|
||||
|
||||
doKey("down");
|
||||
@ -800,6 +942,7 @@ function runTest(testNum) {
|
||||
doKey("return");
|
||||
checkForm("value1");
|
||||
testNum = 599;
|
||||
setTimeout(runTest, 100);
|
||||
break;
|
||||
|
||||
case 600:
|
||||
@ -810,27 +953,59 @@ function runTest(testNum) {
|
||||
checkForm("");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
waitForMenuChange(0);
|
||||
break;
|
||||
|
||||
case 601:
|
||||
checkMenuEntries([]);
|
||||
checkMenuEntries([], testNum);
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(runTest, 50, testNum + 1); // XXX 40ms was too slow, why?
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues) {
|
||||
function addEntry(name, value)
|
||||
{
|
||||
updateFormHistory({ op : "add", fieldname : name, value: value }, runTest);
|
||||
}
|
||||
|
||||
// Runs the next test when scroll event occurs
|
||||
function waitForScroll()
|
||||
{
|
||||
addEventListener("scroll", function() {
|
||||
if (!window.pageYOffset)
|
||||
return;
|
||||
|
||||
removeEventListener("scroll", arguments.callee, false);
|
||||
setTimeout(runTest, 50);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function waitForMenuChange(expectedCount, expectedFirstValue)
|
||||
{
|
||||
if (autocompleteMenu.tree.view.rowCount != expectedCount) {
|
||||
SimpleTest.executeSoon(function () waitForMenuChange(expectedCount, expectedFirstValue));
|
||||
}
|
||||
else if (expectedFirstValue && autocompleteMenu.tree.view.rowCount > 1 &&
|
||||
autocompleteMenu.tree.view.getValueAt(0, autocompleteMenu.tree.columns[0]) != expectedFirstValue) {
|
||||
SimpleTest.executeSoon(function () waitForMenuChange(expectedCount, expectedFirstValue));
|
||||
}
|
||||
else {
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues, testNum) {
|
||||
var actualValues = getMenuEntries();
|
||||
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
|
||||
is(actualValues.length, expectedValues.length, testNum + " Checking length of expected menu");
|
||||
for (var i = 0; i < expectedValues.length; i++)
|
||||
is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
|
||||
is(actualValues[i], expectedValues[i], testNum + " Checking menu entry #"+i);
|
||||
}
|
||||
|
||||
function getMenuEntries() {
|
||||
@ -847,7 +1022,9 @@ function getMenuEntries() {
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
runTest(1);
|
||||
setupFormHistory(function() {
|
||||
runTest();
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
|
@ -44,19 +44,16 @@ Form History test: form field autocomplete
|
||||
|
||||
/** Test for Form History autocomplete **/
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var input = $_(1, "field1");
|
||||
const shiftModifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
|
||||
|
||||
// Get the form history service
|
||||
var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Components.interfaces.nsIFormHistory2);
|
||||
ok(fh != null, "got form history service");
|
||||
|
||||
fh.removeAllEntries();
|
||||
fh.addEntry("field1", "historyvalue");
|
||||
fh.addEntry("field2", "othervalue");
|
||||
function setupFormHistory(aCallback) {
|
||||
updateFormHistory([
|
||||
{ op : "remove" },
|
||||
{ op : "add", fieldname : "field1", value : "historyvalue" },
|
||||
{ op : "add", fieldname : "field2", value : "othervalue" },
|
||||
], aCallback);
|
||||
}
|
||||
|
||||
function setForm(value) {
|
||||
input.value = value;
|
||||
@ -76,19 +73,41 @@ function checkForm(expectedValue) {
|
||||
is(input.value, expectedValue, "Checking " + formID + " input");
|
||||
}
|
||||
|
||||
function nextTest(aTestNum) {
|
||||
setTimeout(runTest, 50, aTestNum + 1); // XXX 40ms was too slow, why?
|
||||
var testNum = 0;
|
||||
var prevValue;
|
||||
var expectingPopup = false;
|
||||
|
||||
function expectPopup()
|
||||
{
|
||||
info("expecting popup for test " + testNum);
|
||||
expectingPopup = true;
|
||||
}
|
||||
|
||||
function popupShownListener()
|
||||
{
|
||||
info("popup shown for test " + testNum);
|
||||
if (expectingPopup) {
|
||||
expectingPopup = false;
|
||||
SimpleTest.executeSoon(runTest);
|
||||
}
|
||||
else {
|
||||
ok(false, "Autocomplete popup not expected during test " + testNum);
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.addAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
|
||||
/*
|
||||
* Main section of test...
|
||||
*
|
||||
* This is a bit hacky, because the events are either being sent or
|
||||
* processed asynchronously, so we need to interrupt our flow with lots of
|
||||
* setTimeout() calls. The case statements are executed in order, one per
|
||||
* timeout.
|
||||
* This is a bit hacky, as many operations happen asynchronously.
|
||||
* Various mechanisms call runTests as a result of operations:
|
||||
* - set expectingPopup to true, and the next test will occur when the autocomplete popup is shown
|
||||
* - call waitForMenuChange(x) to run the next test when the autocomplete popup to have x items in it
|
||||
*/
|
||||
function runTest(testNum) {
|
||||
function runTest() {
|
||||
testNum++;
|
||||
|
||||
// Seems we need to enable this again, or sendKeyEvent() complaints.
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
info("Starting test #" + testNum);
|
||||
@ -98,12 +117,12 @@ function runTest(testNum) {
|
||||
// Make sure initial form is empty.
|
||||
checkForm("");
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
checkMenuEntries(["historyvalue", "PASS1", "PASS2", "final"]);
|
||||
checkMenuEntries(["historyvalue", "PASS1", "PASS2", "final"], testNum);
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkForm(""); // value shouldn't update
|
||||
@ -111,6 +130,7 @@ function runTest(testNum) {
|
||||
checkForm("historyvalue");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -123,6 +143,7 @@ function runTest(testNum) {
|
||||
checkForm("Google");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -136,6 +157,7 @@ function runTest(testNum) {
|
||||
checkForm("Reddit");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -148,6 +170,7 @@ function runTest(testNum) {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("final");
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -156,18 +179,31 @@ function runTest(testNum) {
|
||||
//Delete the first entry (of 3)
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
waitForMenuChange(3);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "historyvalue"), "checking that form history value was deleted");
|
||||
countEntries("field1", "historyvalue",
|
||||
function (num) {
|
||||
ok(!num, testNum + " checking that form history value was deleted");
|
||||
runTest();
|
||||
});
|
||||
break;
|
||||
|
||||
case 8:
|
||||
doKey("return");
|
||||
checkForm("Google")
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
case 7:
|
||||
|
||||
case 9:
|
||||
//Test deletion
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"]);
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
|
||||
// Check the new first entry (of 3)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
@ -178,12 +214,13 @@ function runTest(testNum) {
|
||||
input.value = "";
|
||||
input = $_(3, "field2");
|
||||
testNum = 99;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 100:
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"]);
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkForm(""); // value shouldn't update
|
||||
@ -191,6 +228,7 @@ function runTest(testNum) {
|
||||
checkForm("Google");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -203,6 +241,7 @@ function runTest(testNum) {
|
||||
checkForm("Reddit");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -216,12 +255,13 @@ function runTest(testNum) {
|
||||
checkForm("final");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 103:
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"]);
|
||||
checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkForm(""); // value shouldn't update
|
||||
@ -229,6 +269,7 @@ function runTest(testNum) {
|
||||
checkForm("Google");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -241,6 +282,7 @@ function runTest(testNum) {
|
||||
checkForm("Reddit");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -254,6 +296,7 @@ function runTest(testNum) {
|
||||
checkForm("final");
|
||||
|
||||
testNum = 199;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
@ -278,11 +321,11 @@ function runTest(testNum) {
|
||||
|
||||
// Restore the element.
|
||||
datalist.insertBefore(toRemove, datalist.children[1]);
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
});
|
||||
return;
|
||||
break;
|
||||
|
||||
case 201:
|
||||
// Adding an attribute after the first one while on the first then going
|
||||
@ -300,83 +343,87 @@ function runTest(testNum) {
|
||||
|
||||
// Remove the element.
|
||||
datalist.removeChild(added);
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
});
|
||||
return;
|
||||
break;
|
||||
|
||||
case 202:
|
||||
// Change the first element value attribute.
|
||||
doKey("down");
|
||||
var datalist = document.getElementById('suggest');
|
||||
var prevValue = datalist.children[0].value;
|
||||
prevValue = datalist.children[0].value;
|
||||
datalist.children[0].value = "foo";
|
||||
|
||||
SimpleTest.executeSoon(function() {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("foo");
|
||||
|
||||
datalist.children[0].value = prevValue;
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
});
|
||||
return;
|
||||
expectPopup();
|
||||
break;
|
||||
|
||||
case 203:
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("foo");
|
||||
|
||||
var datalist = document.getElementById('suggest');
|
||||
datalist.children[0].value = prevValue;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 204:
|
||||
// Change the textContent to update the value attribute.
|
||||
doKey("down");
|
||||
var datalist = document.getElementById('suggest');
|
||||
var prevValue = datalist.children[0].getAttribute('value');
|
||||
prevValue = datalist.children[0].getAttribute('value');
|
||||
datalist.children[0].removeAttribute('value');
|
||||
datalist.children[0].textContent = "foobar";
|
||||
expectPopup();
|
||||
break;
|
||||
|
||||
SimpleTest.executeSoon(function() {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("foobar");
|
||||
case 205:
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("foobar");
|
||||
|
||||
datalist.children[0].setAttribute('value', prevValue);
|
||||
testNum = 299;
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
});
|
||||
return;
|
||||
var datalist = document.getElementById('suggest');
|
||||
datalist.children[0].setAttribute('value', prevValue);
|
||||
testNum = 299;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
// Tests for filtering (or not).
|
||||
case 300:
|
||||
// Filters with first letter of the word.
|
||||
synthesizeKey("f", {});
|
||||
setTimeout(function() {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("final");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
}, 500);
|
||||
return;
|
||||
expectPopup();
|
||||
break;
|
||||
|
||||
case 301:
|
||||
// Filter with a leterr in the middle of the word.
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("final");
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 302:
|
||||
// Filter with a letter in the middle of the word.
|
||||
synthesizeKey("i", {});
|
||||
synthesizeKey("n", {});
|
||||
setTimeout(function() {
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("final");
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
nextTest(testNum);
|
||||
}, 500);
|
||||
return;
|
||||
break;
|
||||
|
||||
case 302:
|
||||
case 303:
|
||||
// Filter is disabled with mozNoFilter.
|
||||
input.setAttribute('mozNoFilter', 'true');
|
||||
synthesizeKey("f", {});
|
||||
@ -385,12 +432,11 @@ function runTest(testNum) {
|
||||
doKey("return");
|
||||
checkForm("Google");
|
||||
input.removeAttribute('mozNoFilter');
|
||||
testNum = 399;
|
||||
expectPopup();
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 399;
|
||||
nextTest(testNum);
|
||||
}, 500);
|
||||
return;
|
||||
break;
|
||||
|
||||
case 400:
|
||||
@ -401,28 +447,38 @@ function runTest(testNum) {
|
||||
ok(event.bubbles, "input event should bubble");
|
||||
ok(event.cancelable, "input event should be cancelable");
|
||||
checkForm("Google");
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
}, false);
|
||||
|
||||
doKey("down");
|
||||
checkForm("");
|
||||
doKey("return");
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshown", popupShownListener);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
nextTest(testNum);
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues) {
|
||||
function waitForMenuChange(expectedCount)
|
||||
{
|
||||
if (autocompleteMenu.tree.view.rowCount != expectedCount) {
|
||||
SimpleTest.executeSoon(function () waitForMenuChange(expectedCount));
|
||||
}
|
||||
else {
|
||||
runTest();
|
||||
}
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues, testNum) {
|
||||
var actualValues = getMenuEntries();
|
||||
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
|
||||
is(actualValues.length, expectedValues.length, testNum + " Checking length of expected menu");
|
||||
for (var i = 0; i < expectedValues.length; i++)
|
||||
is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
|
||||
is(actualValues[i], expectedValues[i], testNum + " Checking menu entry #"+i);
|
||||
}
|
||||
|
||||
function getMenuEntries() {
|
||||
@ -439,7 +495,7 @@ function getMenuEntries() {
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
runTest(1);
|
||||
setupFormHistory(runTest);
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
|
@ -293,10 +293,15 @@ var ccNumbers = {
|
||||
],
|
||||
};
|
||||
|
||||
function startTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(!fh.hasEntries, "checking for initially empty storage");
|
||||
function checkInitialState() {
|
||||
countEntries(null, null,
|
||||
function (num) {
|
||||
ok(!num, "checking for initially empty storage");
|
||||
startTest();
|
||||
});
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Fill in values for the various fields. We could just set the <input>'s
|
||||
// value attribute, but we don't save default form values (and we want to
|
||||
// ensure unsaved values are because of autocomplete=off or whatever).
|
||||
@ -369,7 +374,6 @@ function checkSubmit(formNum) {
|
||||
ok(true, "form " + formNum + " submitted");
|
||||
numSubmittedForms++;
|
||||
|
||||
|
||||
// Check for expected storage state.
|
||||
switch (formNum) {
|
||||
// Test 1-24 should not save anything.
|
||||
@ -397,7 +401,12 @@ function checkSubmit(formNum) {
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
ok(!fh.hasEntries, "checking for empty storage");
|
||||
countEntries(null, null,
|
||||
function (num) {
|
||||
ok(!num, "checking for empty storage");
|
||||
submitForm(formNum);
|
||||
});
|
||||
return false;
|
||||
break;
|
||||
case 100:
|
||||
checkForSave("subtest2", "subtestValue", "checking saved subtest value");
|
||||
@ -441,19 +450,24 @@ function checkSubmit(formNum) {
|
||||
break;
|
||||
}
|
||||
|
||||
return submitForm(formNum);
|
||||
}
|
||||
|
||||
function submitForm(formNum)
|
||||
{
|
||||
// Forms 13 and 14 would trigger a save-password notification. Temporarily
|
||||
// disable pwmgr, then reenable it.
|
||||
if (formNum == 12)
|
||||
prefBranch.setBoolPref("signon.rememberSignons", false);
|
||||
SpecialPowers.setBoolPref("signon.rememberSignons", false);
|
||||
if (formNum == 14)
|
||||
prefBranch.clearUserPref("signon.rememberSignons");
|
||||
SpecialPowers.clearUserPref("signon.rememberSignons");
|
||||
|
||||
// Forms 20 and 21 requires browser.formfill.saveHttpsForms to be false
|
||||
if (formNum == 19)
|
||||
prefBranch.setBoolPref("browser.formfill.saveHttpsForms", false);
|
||||
SpecialPowers.setBoolPref("browser.formfill.saveHttpsForms", false);
|
||||
// Reset preference now that 20 and 21 are over
|
||||
if (formNum == 21)
|
||||
prefBranch.clearUserPref("browser.formfill.saveHttpsForms");
|
||||
SpecialPowers.clearUserPref("browser.formfill.saveHttpsForms");
|
||||
|
||||
// End the test now on SeaMonkey.
|
||||
if (formNum == 21 && navigator.userAgent.match(/ SeaMonkey\//)) {
|
||||
@ -470,11 +484,11 @@ function checkSubmit(formNum) {
|
||||
// Form 109 requires browser.formfill.save_https_forms to be true;
|
||||
// Form 110 requires it to be false.
|
||||
if (formNum == 108)
|
||||
prefBranch.setBoolPref("browser.formfill.saveHttpsForms", true);
|
||||
SpecialPowers.setBoolPref("browser.formfill.saveHttpsForms", true);
|
||||
if (formNum == 109)
|
||||
prefBranch.setBoolPref("browser.formfill.saveHttpsForms", false);
|
||||
SpecialPowers.setBoolPref("browser.formfill.saveHttpsForms", false);
|
||||
if (formNum == 110)
|
||||
prefBranch.clearUserPref("browser.formfill.saveHttpsForms");
|
||||
SpecialPowers.clearUserPref("browser.formfill.saveHttpsForms");
|
||||
|
||||
// End the test at the last form.
|
||||
if (formNum == 110) {
|
||||
@ -494,8 +508,6 @@ function checkSubmit(formNum) {
|
||||
//
|
||||
setTimeout(function() {
|
||||
checkObserver.waitForChecks(function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var nextFormNum = formNum == 24 ? 100 : (formNum + 1);
|
||||
|
||||
// Submit the next form. Special cases are Forms 21 and 100, which happen
|
||||
@ -514,18 +526,9 @@ function checkSubmit(formNum) {
|
||||
return false; // cancel current form submission
|
||||
}
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Components.interfaces.nsIFormHistory2);
|
||||
ok(fh != null, "Got formHistory service");
|
||||
|
||||
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
Services.obs.addObserver(checkObserver, "satchel-storage-changed", false);
|
||||
|
||||
window.onload = startTest;
|
||||
window.onload = checkInitialState;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
@ -28,10 +28,15 @@
|
||||
var numSubmittedForms = 0;
|
||||
var numInputFields = 101;
|
||||
|
||||
function startTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(!fh.hasEntries, "checking for initially empty storage");
|
||||
function checkInitialState() {
|
||||
countEntries(null, null,
|
||||
function (num) {
|
||||
ok(!num, "checking for initially empty storage");
|
||||
startTest();
|
||||
});
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
var form = document.getElementById("form1");
|
||||
for (i = 1; i <= numInputFields; i++) {
|
||||
var newField = document.createElement("input");
|
||||
@ -55,8 +60,6 @@ function startTest() {
|
||||
|
||||
// Called by each form's onsubmit handler.
|
||||
function checkSubmit(formNum) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
ok(true, "form " + formNum + " submitted");
|
||||
numSubmittedForms++;
|
||||
|
||||
@ -65,33 +68,14 @@ function checkSubmit(formNum) {
|
||||
checkForSave("test" + i, i, "checking saved value " + i);
|
||||
}
|
||||
|
||||
// End the test at the last form.
|
||||
if (formNum == 1) {
|
||||
is(numSubmittedForms, 1, "Ensuring all forms were submitted.");
|
||||
Services.obs.removeObserver(checkObserver, "satchel-storage-changed");
|
||||
SimpleTest.finish();
|
||||
return false; // return false to cancel current form submission
|
||||
}
|
||||
|
||||
checkObserver.waitForChecks(function() {
|
||||
// submit the next form.
|
||||
var button = getFormSubmitButton(formNum + 1);
|
||||
button.click();
|
||||
});
|
||||
|
||||
return false; // cancel current form submission
|
||||
// End the test.
|
||||
is(numSubmittedForms, 1, "Ensuring all forms were submitted.");
|
||||
SimpleTest.finish();
|
||||
return false; // return false to cancel current form submission
|
||||
}
|
||||
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Components.interfaces.nsIFormHistory2);
|
||||
ok(fh != null, "Got formHistory service");
|
||||
|
||||
window.onload = startTest;
|
||||
|
||||
Services.obs.addObserver(checkObserver, "satchel-storage-changed", false);
|
||||
window.onload = checkInitialState;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
@ -126,10 +126,15 @@
|
||||
var numSubmittedForms = 0;
|
||||
var numInputFields = 101;
|
||||
|
||||
function startTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(!fh.hasEntries, "checking for initially empty storage");
|
||||
function checkInitialState() {
|
||||
countEntries(null, null,
|
||||
function (num) {
|
||||
ok(!num, "checking for initially empty storage");
|
||||
startTest();
|
||||
});
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
// Fill in values for the various fields. We could just set the <input>'s
|
||||
// value attribute, but we don't save default form values (and we want to
|
||||
// ensure unsaved values are because of autocomplete=off or whatever).
|
||||
@ -140,50 +145,42 @@ function startTest() {
|
||||
button.click();
|
||||
}
|
||||
|
||||
// Make sure that the first (numInputFields - 1) were not saved (as they were not changed).
|
||||
// Call done() when finished.
|
||||
function checkCountEntries(formNum, index, done)
|
||||
{
|
||||
countEntries("test" + index, index,
|
||||
function (num) {
|
||||
ok(!num, "checking unsaved value " + index);
|
||||
if (index < numInputFields) {
|
||||
checkCountEntries(formNum, index + 1, done);
|
||||
}
|
||||
else {
|
||||
done(formNum);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Called by each form's onsubmit handler.
|
||||
function checkSubmit(formNum) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
ok(true, "form " + formNum + " submitted");
|
||||
numSubmittedForms++;
|
||||
|
||||
// make sure that the first (numInputFields - 1) were not saved (as they were not changed)
|
||||
for (i = 1; i < numInputFields; i++) {
|
||||
ok(!fh.entryExists("test" + i, i), "checking unsaved value " + i);
|
||||
}
|
||||
|
||||
// make sure that the field # numInputFields was saved
|
||||
checkForSave("test" + numInputFields, numInputFields + " changed", "checking saved value " + numInputFields);
|
||||
|
||||
|
||||
// End the test at the last form.
|
||||
if (formNum == 1) {
|
||||
is(numSubmittedForms, 1, "Ensuring all forms were submitted.");
|
||||
Services.obs.removeObserver(checkObserver, "satchel-storage-changed");
|
||||
SimpleTest.finish();
|
||||
return false; // return false to cancel current form submission
|
||||
}
|
||||
|
||||
checkObserver.waitForChecks(function() {
|
||||
// submit the next form.
|
||||
var button = getFormSubmitButton(formNum + 1);
|
||||
button.click();
|
||||
});
|
||||
checkCountEntries(formNum, 1, checkSubmitCounted);
|
||||
|
||||
return false; // cancel current form submission
|
||||
}
|
||||
|
||||
function checkSubmitCounted(formNum) {
|
||||
is(numSubmittedForms, 1, "Ensuring all forms were submitted.");
|
||||
SimpleTest.finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Components.interfaces.nsIFormHistory2);
|
||||
ok(fh != null, "Got formHistory service");
|
||||
|
||||
window.onload = startTest;
|
||||
|
||||
Services.obs.addObserver(checkObserver, "satchel-storage-changed", false);
|
||||
window.onload = checkInitialState;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2,8 +2,13 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const CURRENT_SCHEMA = 4;
|
||||
const PR_HOURS = 60 * 60 * 1000000;
|
||||
@ -13,6 +18,12 @@ do_get_profile();
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
|
||||
// Send the profile-after-change notification to the form history component to ensure
|
||||
// that it has been initialized.
|
||||
var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
formHistoryStartup.observe(null, "profile-after-change", null);
|
||||
|
||||
function getDBVersion(dbfile) {
|
||||
var ss = Cc["@mozilla.org/storage/service;1"].
|
||||
getService(Ci.mozIStorageService);
|
||||
@ -25,10 +36,67 @@ function getDBVersion(dbfile) {
|
||||
|
||||
const isGUID = /[A-Za-z0-9\+\/]{16}/;
|
||||
|
||||
function getGUIDforID(conn, id) {
|
||||
var stmt = conn.createStatement("SELECT guid from moz_formhistory WHERE id = " + id);
|
||||
stmt.executeStep();
|
||||
var guid = stmt.getString(0);
|
||||
stmt.finalize();
|
||||
return guid;
|
||||
// Find form history entries.
|
||||
function searchEntries(terms, params, iter) {
|
||||
let results = [];
|
||||
FormHistory.search(terms, params, { handleResult: function (result) results.push(result),
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) iter.send(results); }
|
||||
});
|
||||
}
|
||||
|
||||
// Count the number of entries with the given name and value, and call then(number)
|
||||
// when done. If name or value is null, then the value of that field does not matter.
|
||||
function countEntries(name, value, then) {
|
||||
var obj = {};
|
||||
if (name !== null)
|
||||
obj.fieldname = name;
|
||||
if (value !== null)
|
||||
obj.value = value;
|
||||
|
||||
let count = 0;
|
||||
FormHistory.count(obj, { handleResult: function (result) count = result,
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) then(count); }
|
||||
});
|
||||
}
|
||||
|
||||
// Perform a single form history update and call then() when done.
|
||||
function updateEntry(op, name, value, then) {
|
||||
var obj = { op: op };
|
||||
if (name !== null)
|
||||
obj.fieldname = name;
|
||||
if (value !== null)
|
||||
obj.value = value;
|
||||
updateFormHistory(obj, then);
|
||||
}
|
||||
|
||||
// Add a single form history entry with the current time and call then() when done.
|
||||
function addEntry(name, value, then) {
|
||||
let now = Date.now() * 1000;
|
||||
updateFormHistory({ op: "add", fieldname: name, value: value, timesUsed: 1,
|
||||
firstUsed: now, lastUsed: now }, then);
|
||||
}
|
||||
|
||||
// Wrapper around FormHistory.update which handles errors. Calls then() when done.
|
||||
function updateFormHistory(changes, then) {
|
||||
FormHistory.update(changes, { handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) then(); },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs info to the console in the standard way (includes the filename).
|
||||
*
|
||||
* @param aMessage
|
||||
* The message to log to the console.
|
||||
*/
|
||||
function do_log_info(aMessage) {
|
||||
print("TEST-INFO | " + _TEST_FILE + " | " + aMessage);
|
||||
}
|
||||
|
168
toolkit/components/satchel/test/unit/test_async_expire.js
Normal file
168
toolkit/components/satchel/test/unit/test_async_expire.js
Normal file
@ -0,0 +1,168 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var dbFile, oldSize;
|
||||
var currentTestIndex = 0;
|
||||
|
||||
function triggerExpiration() {
|
||||
// We can't easily fake a "daily idle" event, so for testing purposes form
|
||||
// history listens for another notification to trigger an immediate
|
||||
// expiration.
|
||||
Services.obs.notifyObservers(null, "formhistory-expire-now", null);
|
||||
}
|
||||
|
||||
let checkExists = function(num) { do_check_true(num > 0); next_test(); }
|
||||
let checkNotExists = function(num) { do_check_true(!num); next_test(); }
|
||||
|
||||
var TestObserver = {
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
||||
|
||||
observe : function (subject, topic, data) {
|
||||
do_check_eq(topic, "satchel-storage-changed");
|
||||
|
||||
if (data == "formhistory-expireoldentries") {
|
||||
next_test();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function test_finished() {
|
||||
// Make sure we always reset prefs.
|
||||
if (Services.prefs.prefHasUserValue("browser.formfill.expire_days"))
|
||||
Services.prefs.clearUserPref("browser.formfill.expire_days");
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
let iter = tests();
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function tests()
|
||||
{
|
||||
Services.obs.addObserver(TestObserver, "satchel-storage-changed", true);
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("asyncformhistory_expire.sqlite");
|
||||
var profileDir = do_get_profile();
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
dbFile = profileDir.clone();
|
||||
dbFile.append("formhistory.sqlite");
|
||||
if (dbFile.exists())
|
||||
dbFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_true(dbFile.exists());
|
||||
|
||||
// We're going to clear this at the end, so it better have the default value now.
|
||||
do_check_false(Services.prefs.prefHasUserValue("browser.formfill.expire_days"));
|
||||
|
||||
// Sanity check initial state
|
||||
yield countEntries(null, null, function(num) { do_check_eq(508, num); next_test(); });
|
||||
yield countEntries("name-A", "value-A", checkExists); // lastUsed == distant past
|
||||
yield countEntries("name-B", "value-B", checkExists); // lastUsed == distant future
|
||||
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
|
||||
// Add a new entry
|
||||
yield countEntries("name-C", "value-C", checkNotExists);
|
||||
yield addEntry("name-C", "value-C", next_test);
|
||||
yield countEntries("name-C", "value-C", checkExists);
|
||||
|
||||
// Update some existing entries to have ages relative to when the test runs.
|
||||
var now = 1000 * Date.now();
|
||||
let updateLastUsed = function updateLastUsedFn(results, age)
|
||||
{
|
||||
let lastUsed = now - age * 24 * PR_HOURS;
|
||||
|
||||
let changes = [ ];
|
||||
for (let r = 0; r < results.length; r++) {
|
||||
changes.push({ op: "update", lastUsed: lastUsed, guid: results[r].guid });
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
let results = yield searchEntries(["guid"], { lastUsed: 181 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 181), next_test);
|
||||
|
||||
results = yield searchEntries(["guid"], { lastUsed: 179 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 179), next_test);
|
||||
|
||||
results = yield searchEntries(["guid"], { lastUsed: 31 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 31), next_test);
|
||||
|
||||
results = yield searchEntries(["guid"], { lastUsed: 29 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 29), next_test);
|
||||
|
||||
results = yield searchEntries(["guid"], { lastUsed: 9999 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 11), next_test);
|
||||
|
||||
results = yield searchEntries(["guid"], { lastUsed: 9 }, iter);
|
||||
yield updateFormHistory(updateLastUsed(results, 9), next_test);
|
||||
|
||||
yield countEntries("name-A", "value-A", checkExists);
|
||||
yield countEntries("181DaysOld", "foo", checkExists);
|
||||
yield countEntries("179DaysOld", "foo", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(509, num); next_test(); });
|
||||
|
||||
// 2 entries are expected to expire.
|
||||
triggerExpiration();
|
||||
yield;
|
||||
|
||||
yield countEntries("name-A", "value-A", checkNotExists);
|
||||
yield countEntries("181DaysOld", "foo", checkNotExists);
|
||||
yield countEntries("179DaysOld", "foo", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(507, num); next_test(); });
|
||||
|
||||
// And again. No change expected.
|
||||
triggerExpiration();
|
||||
yield;
|
||||
|
||||
yield countEntries(null, null, function(num) { do_check_eq(507, num); next_test(); });
|
||||
|
||||
// Set formfill pref to 30 days.
|
||||
Services.prefs.setIntPref("browser.formfill.expire_days", 30);
|
||||
yield countEntries("179DaysOld", "foo", checkExists);
|
||||
yield countEntries("bar", "31days", checkExists);
|
||||
yield countEntries("bar", "29days", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(507, num); next_test(); });
|
||||
|
||||
triggerExpiration();
|
||||
yield;
|
||||
|
||||
yield countEntries("179DaysOld", "foo", checkNotExists);
|
||||
yield countEntries("bar", "31days", checkNotExists);
|
||||
yield countEntries("bar", "29days", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(505, num); next_test(); });
|
||||
|
||||
// Set override pref to 10 days and expire. This expires a large batch of
|
||||
// entries, and should trigger a VACCUM to reduce file size.
|
||||
Services.prefs.setIntPref("browser.formfill.expire_days", 10);
|
||||
|
||||
yield countEntries("bar", "29days", checkExists);
|
||||
yield countEntries("9DaysOld", "foo", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(505, num); next_test(); });
|
||||
|
||||
triggerExpiration();
|
||||
yield;
|
||||
|
||||
yield countEntries("bar", "29days", checkNotExists);
|
||||
yield countEntries("9DaysOld", "foo", checkExists);
|
||||
yield countEntries("name-B", "value-B", checkExists);
|
||||
yield countEntries("name-C", "value-C", checkExists);
|
||||
yield countEntries(null, null, function(num) { do_check_eq(3, num); next_test(); });
|
||||
|
||||
test_finished();
|
||||
};
|
@ -3,19 +3,12 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
var fac;
|
||||
var prefs;
|
||||
|
||||
const DEFAULT_EXPIRE_DAYS = 180;
|
||||
let numRecords, timeGroupingSize, now;
|
||||
|
||||
function countAllEntries() {
|
||||
let stmt = fh.DBConnection.createStatement("SELECT COUNT(*) as numEntries FROM moz_formhistory");
|
||||
do_check_true(stmt.executeStep());
|
||||
let numEntries = stmt.row.numEntries;
|
||||
stmt.finalize();
|
||||
return numEntries;
|
||||
}
|
||||
const DEFAULT_EXPIRE_DAYS = 180;
|
||||
|
||||
function padLeft(number, length) {
|
||||
var str = number + '';
|
||||
@ -24,7 +17,7 @@ function padLeft(number, length) {
|
||||
return str;
|
||||
}
|
||||
|
||||
function getFormExpiryDays () {
|
||||
function getFormExpiryDays() {
|
||||
if (prefs.prefHasUserValue("browser.formfill.expire_days"))
|
||||
return prefs.getIntPref("browser.formfill.expire_days");
|
||||
else
|
||||
@ -32,205 +25,213 @@ function getFormExpiryDays () {
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
try {
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_autocomplete.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_autocomplete.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].
|
||||
getService(Ci.nsIFormAutoComplete);
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].
|
||||
getService(Ci.nsIFormAutoComplete);
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
timeGroupingSize = prefs.getIntPref("browser.formfill.timeGroupingSize") * 1000 * 1000;
|
||||
|
||||
var timeGroupingSize = prefs.getIntPref("browser.formfill.timeGroupingSize") * 1000 * 1000;
|
||||
var maxTimeGroupings = prefs.getIntPref("browser.formfill.maxTimeGroupings");
|
||||
var bucketSize = prefs.getIntPref("browser.formfill.bucketSize");
|
||||
|
||||
// ===== Tests with constant timesUsed and varying lastUsed date =====
|
||||
// insert 2 records per bucket to check alphabetical sort within
|
||||
var now = 1000 * Date.now();
|
||||
var numRecords = Math.ceil(maxTimeGroupings / bucketSize) * 2;
|
||||
|
||||
fh.DBConnection.beginTransaction();
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
let useDate = now - (i/2 * bucketSize * timeGroupingSize);
|
||||
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field1', " +
|
||||
"'value" + padLeft(numRecords - 1 - i, 2) + "', " +
|
||||
"1, " +
|
||||
(useDate + 1) + ", " +
|
||||
(useDate + 1) +
|
||||
");");
|
||||
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field1', " +
|
||||
"'value" + padLeft(numRecords - 2 - i, 2) + "', " +
|
||||
"1, " +
|
||||
useDate + ", " +
|
||||
useDate +
|
||||
");");
|
||||
}
|
||||
fh.DBConnection.commitTransaction();
|
||||
|
||||
// ===== 1 =====
|
||||
// Check initial state is as expected
|
||||
testnum++;
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_eq(numRecords, countAllEntries());
|
||||
do_check_true(fh.nameExists("field1"));
|
||||
|
||||
// ===== 2 =====
|
||||
// Check search contains all entries
|
||||
testnum++;
|
||||
var results = fac.autoCompleteSearch("field1", "", null, null);
|
||||
do_check_eq(numRecords, results.matchCount);
|
||||
|
||||
// ===== 3 =====
|
||||
// Check search result ordering with empty search term
|
||||
testnum++;
|
||||
results = fac.autoCompleteSearch("field1", "", null, null);
|
||||
let lastFound = numRecords;
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
do_check_eq(parseInt(results.getValueAt(i + 1).substr(5), 10), --lastFound);
|
||||
do_check_eq(parseInt(results.getValueAt(i).substr(5), 10), --lastFound);
|
||||
}
|
||||
|
||||
// ===== 4 =====
|
||||
// Check search result ordering with "v"
|
||||
testnum++;
|
||||
results = fac.autoCompleteSearch("field1", "v", null, null);
|
||||
lastFound = numRecords;
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
do_check_eq(parseInt(results.getValueAt(i + 1).substr(5), 10), --lastFound);
|
||||
do_check_eq(parseInt(results.getValueAt(i).substr(5), 10), --lastFound);
|
||||
}
|
||||
|
||||
// ===== Tests with constant use dates and varying timesUsed =====
|
||||
|
||||
let timesUsedSamples = 20;
|
||||
fh.DBConnection.beginTransaction();
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
let timesUsed = (timesUsedSamples - i);
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field2', "+
|
||||
"'value" + (timesUsedSamples - 1 - i) + "', " +
|
||||
timesUsed * timeGroupingSize + ", " +
|
||||
now + ", " +
|
||||
now +
|
||||
");");
|
||||
}
|
||||
fh.DBConnection.commitTransaction();
|
||||
|
||||
// ===== 5 =====
|
||||
// Check search result ordering with empty search term
|
||||
testnum++;
|
||||
results = fac.autoCompleteSearch("field2", "", null, null);
|
||||
lastFound = timesUsedSamples;
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
do_check_eq(parseInt(results.getValueAt(i).substr(5)), --lastFound);
|
||||
}
|
||||
|
||||
// ===== 6 =====
|
||||
// Check search result ordering with "v"
|
||||
testnum++;
|
||||
results = fac.autoCompleteSearch("field2", "v", null, null);
|
||||
lastFound = timesUsedSamples;
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
do_check_eq(parseInt(results.getValueAt(i).substr(5)), --lastFound);
|
||||
}
|
||||
|
||||
// ===== 7 =====
|
||||
// Check that "senior citizen" entries get a bonus (browser.formfill.agedBonus)
|
||||
testnum++;
|
||||
|
||||
let agedDate = 1000 * (Date.now() - getFormExpiryDays() * 24 * 60 * 60 * 1000);
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field3', " +
|
||||
"'old but not senior', " +
|
||||
"100, " +
|
||||
(agedDate + 60 * 1000 * 1000) + ", " +
|
||||
now +
|
||||
");");
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field3', " +
|
||||
"'senior citizen', " +
|
||||
"100, " +
|
||||
(agedDate - 60 * 1000 * 1000) + ", " +
|
||||
now +
|
||||
");");
|
||||
|
||||
results = fac.autoCompleteSearch("field3", "", null, null);
|
||||
do_check_eq(results.getValueAt(0), "senior citizen");
|
||||
do_check_eq(results.getValueAt(1), "old but not senior");
|
||||
|
||||
// ===== 8 =====
|
||||
// Check entries that are really old or in the future
|
||||
testnum++;
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field4', " +
|
||||
"'date of 0', " +
|
||||
"1, " +
|
||||
0 + ", " +
|
||||
0 +
|
||||
");");
|
||||
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field4', " +
|
||||
"'in the future 1', " +
|
||||
"1, " +
|
||||
0 + ", " +
|
||||
(now * 2) +
|
||||
");");
|
||||
|
||||
fh.DBConnection.executeSimpleSQL(
|
||||
"INSERT INTO moz_formhistory "+
|
||||
"(fieldname, value, timesUsed, firstUsed, lastUsed) " +
|
||||
"VALUES ("+
|
||||
"'field4', " +
|
||||
"'in the future 2', " +
|
||||
"1, " +
|
||||
(now * 2) + ", " +
|
||||
(now * 2) +
|
||||
");");
|
||||
|
||||
results = fac.autoCompleteSearch("field4", "", null, null);
|
||||
do_check_eq(results.matchCount, 3);
|
||||
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test0() {
|
||||
var maxTimeGroupings = prefs.getIntPref("browser.formfill.maxTimeGroupings");
|
||||
var bucketSize = prefs.getIntPref("browser.formfill.bucketSize");
|
||||
|
||||
// ===== Tests with constant timesUsed and varying lastUsed date =====
|
||||
// insert 2 records per bucket to check alphabetical sort within
|
||||
now = 1000 * Date.now();
|
||||
numRecords = Math.ceil(maxTimeGroupings / bucketSize) * 2;
|
||||
|
||||
let changes = [ ];
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
let useDate = now - (i/2 * bucketSize * timeGroupingSize);
|
||||
|
||||
changes.push({ op : "add", fieldname: "field1", value: "value" + padLeft(numRecords - 1 - i, 2),
|
||||
timesUsed: 1, firstUsed: useDate, lastUsed: useDate });
|
||||
changes.push({ op : "add", fieldname: "field1", value: "value" + padLeft(numRecords - 2 - i, 2),
|
||||
timesUsed: 1, firstUsed: useDate, lastUsed: useDate });
|
||||
}
|
||||
|
||||
updateFormHistory(changes, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test1() {
|
||||
do_log_info("Check initial state is as expected");
|
||||
|
||||
countEntries(null, null, function (count) {
|
||||
countEntries("field1", null, function (count) {
|
||||
do_check_true(count > 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test2() {
|
||||
do_log_info("Check search contains all entries");
|
||||
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
do_check_eq(numRecords, aResults.matchCount);
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test3() {
|
||||
do_log_info("Check search result ordering with empty search term");
|
||||
|
||||
let lastFound = numRecords;
|
||||
fac.autoCompleteSearchAsync("field1", "", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
do_check_eq(parseInt(aResults.getValueAt(i + 1).substr(5), 10), --lastFound);
|
||||
do_check_eq(parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test4() {
|
||||
do_log_info("Check search result ordering with \"v\"");
|
||||
|
||||
let lastFound = numRecords;
|
||||
fac.autoCompleteSearchAsync("field1", "v", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
for (let i = 0; i < numRecords; i+=2) {
|
||||
do_check_eq(parseInt(aResults.getValueAt(i + 1).substr(5), 10), --lastFound);
|
||||
do_check_eq(parseInt(aResults.getValueAt(i).substr(5), 10), --lastFound);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const timesUsedSamples = 20;
|
||||
|
||||
add_test(function test5() {
|
||||
do_log_info("Begin tests with constant use dates and varying timesUsed");
|
||||
|
||||
let changes = [];
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
let timesUsed = (timesUsedSamples - i);
|
||||
let change = { op : "add", fieldname: "field2", value: "value" + (timesUsedSamples - 1 - i),
|
||||
timesUsed: timesUsed * timeGroupingSize, firstUsed: now, lastUsed: now };
|
||||
changes.push(change);
|
||||
}
|
||||
updateFormHistory(changes, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test6() {
|
||||
do_log_info("Check search result ordering with empty search term");
|
||||
|
||||
let lastFound = timesUsedSamples;
|
||||
fac.autoCompleteSearchAsync("field2", "", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
do_check_eq(parseInt(aResults.getValueAt(i).substr(5)), --lastFound);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test7() {
|
||||
do_log_info("Check search result ordering with \"v\"");
|
||||
|
||||
let lastFound = timesUsedSamples;
|
||||
fac.autoCompleteSearchAsync("field2", "v", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
for (let i = 0; i < timesUsedSamples; i++) {
|
||||
do_check_eq(parseInt(aResults.getValueAt(i).substr(5)), --lastFound);
|
||||
}
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test8() {
|
||||
do_log_info("Check that \"senior citizen\" entries get a bonus (browser.formfill.agedBonus)");
|
||||
|
||||
let agedDate = 1000 * (Date.now() - getFormExpiryDays() * 24 * 60 * 60 * 1000);
|
||||
|
||||
let changes = [ ];
|
||||
changes.push({ op : "add", fieldname: "field3", value: "old but not senior",
|
||||
timesUsed: 100, firstUsed: (agedDate + 60 * 1000 * 1000), lastUsed: now });
|
||||
changes.push({ op : "add", fieldname: "field3", value: "senior citizen",
|
||||
timesUsed: 100, firstUsed: (agedDate - 60 * 1000 * 1000), lastUsed: now });
|
||||
updateFormHistory(changes, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test9() {
|
||||
fac.autoCompleteSearchAsync("field3", "", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
do_check_eq(aResults.getValueAt(0), "senior citizen");
|
||||
do_check_eq(aResults.getValueAt(1), "old but not senior");
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test10() {
|
||||
do_log_info("Check entries that are really old or in the future");
|
||||
|
||||
let changes = [ ];
|
||||
changes.push({ op : "add", fieldname: "field4", value: "date of 0",
|
||||
timesUsed: 1, firstUsed: 0, lastUsed: 0 });
|
||||
changes.push({ op : "add", fieldname: "field4", value: "in the future 1",
|
||||
timesUsed: 1, firstUsed: 0, lastUsed: now * 2 });
|
||||
changes.push({ op : "add", fieldname: "field4", value: "in the future 2",
|
||||
timesUsed: 1, firstUsed: now * 2, lastUsed: now * 2 });
|
||||
updateFormHistory(changes, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test11() {
|
||||
fac.autoCompleteSearchAsync("field4", "", null, null, {
|
||||
onSearchCompletion : function(aResults) {
|
||||
do_check_eq(aResults.matchCount, 3);
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let syncValues = ["sync1", "sync1a", "sync2", "sync3"]
|
||||
|
||||
add_test(function test12() {
|
||||
do_log_info("Check old synchronous api");
|
||||
|
||||
let changes = [ ];
|
||||
for (let value of syncValues) {
|
||||
changes.push({ op : "add", fieldname: "field5", value: value });
|
||||
}
|
||||
updateFormHistory(changes, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test13() {
|
||||
let autocompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
|
||||
let results = autocompleteService.autoCompleteSearch("field5", "", null, null);
|
||||
do_check_eq(results.matchCount, syncValues.length, "synchronous matchCount");
|
||||
for (let i = 0; i < results.matchCount; i++) {
|
||||
do_check_eq(results.getValueAt(i), syncValues[i]);
|
||||
}
|
||||
|
||||
let results = autocompleteService.autoCompleteSearch("field5", "sync1", null, null);
|
||||
do_check_eq(results.matchCount, 2, "synchronous matchCount");
|
||||
do_check_eq(results.getValueAt(0), "sync1");
|
||||
do_check_eq(results.getValueAt(1), "sync1a");
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -2,65 +2,88 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
var testnum = 0;
|
||||
let bakFile;
|
||||
|
||||
function run_test() {
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_CORRUPT.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
let testfile = do_get_file("formhistory_CORRUPT.sqlite");
|
||||
let profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
let destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
var bakFile = profileDir.clone();
|
||||
bakFile = profileDir.clone();
|
||||
bakFile.append("formhistory.sqlite.corrupt");
|
||||
if (bakFile.exists())
|
||||
bakFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_corruptFormHistoryDB_lazyCorruptInit1() {
|
||||
do_log_info("ensure FormHistory backs up a corrupt DB on initialization.");
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
// Open the DB, ensure that a backup of the corrupt DB is made.
|
||||
do_check_false(bakFile.exists());
|
||||
var fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
// DB init is done lazily so the DB shouldn't be created yet.
|
||||
do_check_false(bakFile.exists());
|
||||
// Doing any request to the DB should create it.
|
||||
fh.DBConnection;
|
||||
countEntries(null, null, run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test_corruptFormHistoryDB_lazyCorruptInit2() {
|
||||
do_check_true(bakFile.exists());
|
||||
bakFile.remove(false);
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
// File should be empty
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
// ===== 3 =====
|
||||
testnum++;
|
||||
// Try adding an entry
|
||||
fh.addEntry("name-A", "value-A");
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
add_test(function test_corruptFormHistoryDB_emptyInit() {
|
||||
do_log_info("test that FormHistory initializes an empty DB in place of corrupt DB.");
|
||||
|
||||
FormHistory.count({}, {
|
||||
handleResult : function(aNumEntries) {
|
||||
do_check_true(aNumEntries == 0);
|
||||
FormHistory.count({ fieldname : "name-A", value : "value-A" }, {
|
||||
handleResult : function(aNumEntries2) {
|
||||
do_check_true(aNumEntries2 == 0);
|
||||
run_next_test();
|
||||
},
|
||||
handleError : function(aError2) {
|
||||
do_throw("DB initialized after reading a corrupt DB file found an entry.");
|
||||
}
|
||||
});
|
||||
},
|
||||
handleError : function (aError) {
|
||||
do_throw("DB initialized after reading a corrupt DB file is not empty.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ===== 4 =====
|
||||
testnum++;
|
||||
// Try removing an entry
|
||||
fh.removeEntry("name-A", "value-A");
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
add_test(function test_corruptFormHistoryDB_addEntry() {
|
||||
do_log_info("test adding an entry to the empty DB.");
|
||||
|
||||
updateEntry("add", "name-A", "value-A",
|
||||
function() {
|
||||
countEntries("name-A", "value-A",
|
||||
function(count) {
|
||||
do_check_true(count == 1);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
||||
add_test(function test_corruptFormHistoryDB_removeEntry() {
|
||||
do_log_info("test removing an entry to the empty DB.");
|
||||
|
||||
updateEntry("remove", "name-A", "value-A",
|
||||
function() {
|
||||
countEntries("name-A", "value-A",
|
||||
function(count) {
|
||||
do_check_true(count == 0);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,143 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Returns true if the timestamp is within 30 seconds of now.
|
||||
function is_about_now(timestamp) {
|
||||
var delta = Math.abs(timestamp - 1000 * Date.now());
|
||||
var seconds = 30 * 1000000;
|
||||
return delta < seconds;
|
||||
}
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
var timesUsed, firstUsed, lastUsed;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v0.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(0, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
// Check for expected contents.
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C1"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C2"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
// Check that timestamps were created correctly.
|
||||
|
||||
var query = "SELECT timesUsed, firstUsed, lastUsed " +
|
||||
"FROM moz_formhistory WHERE fieldname = 'name-A'";
|
||||
var stmt = fh.DBConnection.createStatement(query);
|
||||
stmt.executeStep();
|
||||
|
||||
timesUsed = stmt.getInt32(0);
|
||||
firstUsed = stmt.getInt64(1);
|
||||
lastUsed = stmt.getInt64(2);
|
||||
stmt.finalize();
|
||||
|
||||
do_check_eq(1, timesUsed);
|
||||
do_check_true(firstUsed == lastUsed);
|
||||
// Upgraded entries timestamped 24 hours in the past.
|
||||
do_check_true(is_about_now(firstUsed + 24 * PR_HOURS));
|
||||
|
||||
|
||||
// ===== 3 =====
|
||||
testnum++;
|
||||
// Exercise adding and removing a name/value pair
|
||||
do_check_false(fh.entryExists("name-D", "value-D"));
|
||||
fh.addEntry("name-D", "value-D");
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
fh.removeEntry("name-D", "value-D");
|
||||
do_check_false(fh.entryExists("name-D", "value-D"));
|
||||
|
||||
|
||||
// ===== 4 =====
|
||||
testnum++;
|
||||
// Add a new entry, check expected properties
|
||||
do_check_false(fh.entryExists("name-E", "value-E"));
|
||||
fh.addEntry("name-E", "value-E");
|
||||
do_check_true(fh.entryExists("name-E", "value-E"));
|
||||
|
||||
query = "SELECT timesUsed, firstUsed, lastUsed " +
|
||||
"FROM moz_formhistory WHERE fieldname = 'name-E'";
|
||||
stmt = fh.DBConnection.createStatement(query);
|
||||
stmt.executeStep();
|
||||
|
||||
timesUsed = stmt.getInt32(0);
|
||||
firstUsed = stmt.getInt64(1);
|
||||
lastUsed = stmt.getInt64(2);
|
||||
stmt.finalize();
|
||||
|
||||
do_check_eq(1, timesUsed);
|
||||
do_check_true(firstUsed == lastUsed);
|
||||
do_check_true(is_about_now(firstUsed));
|
||||
|
||||
// The next test adds the entry again, and check to see that the lastUsed
|
||||
// field is updated. Unfortunately, on Windows PR_Now() is granular
|
||||
// (robarnold says usually 16.5ms, sometimes 10ms), so if we execute the
|
||||
// test too soon the timestamp will be the same! So, we'll wait a short
|
||||
// period of time to make sure the timestamp will differ.
|
||||
do_test_pending();
|
||||
do_timeout(50, delayed_test);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
||||
|
||||
function delayed_test() {
|
||||
try {
|
||||
|
||||
// ===== 5 =====
|
||||
testnum++;
|
||||
// Add entry again, check for updated properties.
|
||||
do_check_true(fh.entryExists("name-E", "value-E"));
|
||||
fh.addEntry("name-E", "value-E");
|
||||
do_check_true(fh.entryExists("name-E", "value-E"));
|
||||
|
||||
var query = "SELECT timesUsed, firstUsed, lastUsed " +
|
||||
"FROM moz_formhistory WHERE fieldname = 'name-E'";
|
||||
var stmt = fh.DBConnection.createStatement(query);
|
||||
stmt.executeStep();
|
||||
|
||||
timesUsed = stmt.getInt32(0);
|
||||
var firstUsed2 = stmt.getInt64(1);
|
||||
var lastUsed2 = stmt.getInt64(2);
|
||||
stmt.finalize();
|
||||
|
||||
do_check_eq(2, timesUsed);
|
||||
do_check_true(is_about_now(lastUsed2));
|
||||
|
||||
do_check_true(firstUsed == firstUsed2); //unchanged
|
||||
do_check_true(lastUsed != lastUsed2); //changed
|
||||
do_check_true(firstUsed2 != lastUsed2);
|
||||
|
||||
do_test_finished();
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Returns true if the timestamp is within 30 seconds of now.
|
||||
function is_about_now(timestamp) {
|
||||
var delta = Math.abs(timestamp - 1000 * Date.now());
|
||||
var seconds = 30 * 1000000;
|
||||
return delta < seconds;
|
||||
}
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
var timesUsed, firstUsed, lastUsed;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v0v1.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(0, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
// Check for expected contents.
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C1"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C2"));
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
|
||||
// The name-D entry was added by v0 code, so no timestamps were set. Make
|
||||
// sure the upgrade set timestamps on that entry.
|
||||
var query = "SELECT timesUsed, firstUsed, lastUsed " +
|
||||
"FROM moz_formhistory WHERE fieldname = 'name-D'";
|
||||
var stmt = fh.DBConnection.createStatement(query);
|
||||
stmt.executeStep();
|
||||
|
||||
timesUsed = stmt.getInt32(0);
|
||||
firstUsed = stmt.getInt64(1);
|
||||
lastUsed = stmt.getInt64(2);
|
||||
stmt.finalize();
|
||||
|
||||
do_check_eq(1, timesUsed);
|
||||
do_check_true(firstUsed == lastUsed);
|
||||
// Upgraded entries timestamped 24 hours in the past.
|
||||
do_check_true(is_about_now(firstUsed + 24 * PR_HOURS));
|
||||
|
||||
|
||||
// ===== 3 =====
|
||||
testnum++;
|
||||
|
||||
// Check to make sure the existing timestamps are unmodified.
|
||||
var query = "SELECT timesUsed, firstUsed, lastUsed " +
|
||||
"FROM moz_formhistory WHERE fieldname = 'name-A'";
|
||||
var stmt = fh.DBConnection.createStatement(query);
|
||||
stmt.executeStep();
|
||||
|
||||
timesUsed = stmt.getInt32(0);
|
||||
firstUsed = stmt.getInt64(1);
|
||||
lastUsed = stmt.getInt64(2);
|
||||
stmt.finalize();
|
||||
|
||||
do_check_eq(1, timesUsed);
|
||||
do_check_eq(lastUsed, 1231984073012182);
|
||||
do_check_eq(firstUsed, 1231984073012182);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v1.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(1, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added (which is all the v2 upgrade does)
|
||||
do_check_true(fh.DBConnection.indexExists("moz_formhistory_lastused_index"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
// Check that old table was removed
|
||||
do_check_false(fh.DBConnection.tableExists("moz_dummy_table"));
|
||||
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
|
||||
// Just sanity check for expected contents and that DB is working.
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_false(fh.entryExists("name-B", "value-B"));
|
||||
fh.addEntry("name-B", "value-B");
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
fh.removeEntry("name-B", "value-B");
|
||||
do_check_false(fh.entryExists("name-B", "value-B"));
|
||||
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v1v2.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(1, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added / still exists (which is all the v2 upgrade does)
|
||||
do_check_true(fh.DBConnection.indexExists("moz_formhistory_lastused_index"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
// Check that old table was removed
|
||||
do_check_false(fh.DBConnection.tableExists("moz_dummy_table"));
|
||||
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
|
||||
// Just sanity check for expected contents and that DB is working.
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_false(fh.entryExists("name-B", "value-B"));
|
||||
fh.addEntry("name-B", "value-B");
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
fh.removeEntry("name-B", "value-B");
|
||||
do_check_false(fh.entryExists("name-B", "value-B"));
|
||||
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v2.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(2, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(fh.DBConnection.indexExists("moz_formhistory_guid_index"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
var guid = getGUIDforID(fh.DBConnection, 1);
|
||||
do_check_true(isGUID.test(guid));
|
||||
|
||||
// Add a new entry and check that it gets a GUID
|
||||
do_check_false(fh.entryExists("name-B", "value-B"));
|
||||
fh.addEntry("name-B", "value-B");
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
|
||||
guid = getGUIDforID(fh.DBConnection, 2);
|
||||
do_check_true(isGUID.test(guid));
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_v2v3.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
if (destFile.exists())
|
||||
destFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(2, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(fh.DBConnection.indexExists("moz_formhistory_guid_index"));
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
|
||||
// Entry added by v3 code, has a GUID that shouldn't be changed.
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
var guid = getGUIDforID(fh.DBConnection, 1);
|
||||
do_check_eq(guid, "dgdaRfzsTnOOZ7wK");
|
||||
|
||||
// Entry added by v2 code after a downgrade, GUID should be assigned on upgrade.
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
guid = getGUIDforID(fh.DBConnection, 2);
|
||||
do_check_true(isGUID.test(guid));
|
||||
|
||||
// Add a new entry and check that it gets a GUID
|
||||
do_check_false(fh.entryExists("name-C", "value-C"));
|
||||
fh.addEntry("name-C", "value-C");
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
|
||||
guid = getGUIDforID(fh.DBConnection, 3);
|
||||
do_check_true(isGUID.test(guid));
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
@ -3,9 +3,17 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
let iter;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
iter = next_test();
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
try {
|
||||
|
||||
@ -22,19 +30,29 @@ function run_test()
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(3, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(fh.DBConnection.tableExists("moz_deleted_formhistory"));
|
||||
destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
let dbConnection = Services.storage.openUnsharedDatabase(destFile);
|
||||
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(dbConnection.tableExists("moz_deleted_formhistory"));
|
||||
dbConnection.close();
|
||||
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
// check that an entry still exists
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
yield countEntries("name-A", "value-A",
|
||||
function (num) {
|
||||
do_check_true(num > 0);
|
||||
do_test_finished();
|
||||
}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
|
@ -3,9 +3,17 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
|
||||
let iter;
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
iter = next_test();
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
try {
|
||||
|
||||
@ -22,19 +30,27 @@ function run_test()
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(3, getDBVersion(testfile));
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(fh.DBConnection.tableExists("moz_deleted_formhistory"));
|
||||
destFile = profileDir.clone();
|
||||
destFile.append("formhistory.sqlite");
|
||||
dbConnection = Services.storage.openUnsharedDatabase(destFile);
|
||||
|
||||
// check for upgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
|
||||
// Check that the index was added
|
||||
do_check_true(dbConnection.tableExists("moz_deleted_formhistory"));
|
||||
dbConnection.close();
|
||||
|
||||
// check that an entry still exists
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
yield countEntries("name-A", "value-A",
|
||||
function (num) {
|
||||
do_check_true(num > 0);
|
||||
do_test_finished();
|
||||
}
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
|
@ -11,7 +11,20 @@
|
||||
* Part B tests this when the columns do *not* match, so the DB is reset.
|
||||
*/
|
||||
|
||||
let iter = tests();
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function tests()
|
||||
{
|
||||
try {
|
||||
var testnum = 0;
|
||||
@ -29,32 +42,34 @@ function run_test()
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(999, getDBVersion(testfile));
|
||||
|
||||
var fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
let checkZero = function(num) { do_check_eq(num, 0); next_test(); }
|
||||
let checkOne = function(num) { do_check_eq(num, 1); next_test(); }
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
// Check for expected contents.
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C1"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C2"));
|
||||
do_check_true(fh.entryExists("name-E", "value-E"));
|
||||
yield countEntries(null, null, function(num) { do_check_true(num > 0); next_test(); });
|
||||
yield countEntries("name-A", "value-A", checkOne);
|
||||
yield countEntries("name-B", "value-B", checkOne);
|
||||
yield countEntries("name-C", "value-C1", checkOne);
|
||||
yield countEntries("name-C", "value-C2", checkOne);
|
||||
yield countEntries("name-E", "value-E", checkOne);
|
||||
|
||||
// check for downgraded schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
// Exercise adding and removing a name/value pair
|
||||
do_check_false(fh.entryExists("name-D", "value-D"));
|
||||
fh.addEntry("name-D", "value-D");
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
fh.removeEntry("name-D", "value-D");
|
||||
do_check_false(fh.entryExists("name-D", "value-D"));
|
||||
yield countEntries("name-D", "value-D", checkZero);
|
||||
yield updateEntry("add", "name-D", "value-D", next_test);
|
||||
yield countEntries("name-D", "value-D", checkOne);
|
||||
yield updateEntry("remove", "name-D", "value-D", next_test);
|
||||
yield countEntries("name-D", "value-D", checkZero);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
@ -11,7 +11,20 @@
|
||||
* Part B tests this when the columns do *not* match, so the DB is reset.
|
||||
*/
|
||||
|
||||
let iter = tests();
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_test_pending();
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
iter.next();
|
||||
}
|
||||
|
||||
function tests()
|
||||
{
|
||||
try {
|
||||
var testnum = 0;
|
||||
@ -34,43 +47,46 @@ function run_test()
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_eq(999, getDBVersion(testfile));
|
||||
|
||||
let checkZero = function(num) { do_check_eq(num, 0); next_test(); }
|
||||
let checkOne = function(num) { do_check_eq(num, 1); next_test(); }
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Open the DB, ensure that a backup of the corrupt DB is made.
|
||||
do_check_false(bakFile.exists());
|
||||
var fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
// DB init is done lazily so the DB shouldn't be created yet.
|
||||
do_check_false(bakFile.exists());
|
||||
// Doing any request to the DB should create it.
|
||||
fh.DBConnection;
|
||||
yield countEntries("", "", next_test);
|
||||
|
||||
do_check_true(bakFile.exists());
|
||||
bakFile.remove(false);
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
// File should be empty
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
yield countEntries(null, null, function(num) { do_check_false(num); next_test(); });
|
||||
yield countEntries("name-A", "value-A", checkZero);
|
||||
// check for current schema.
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
do_check_eq(CURRENT_SCHEMA, FormHistory.schemaVersion);
|
||||
|
||||
// ===== 3 =====
|
||||
testnum++;
|
||||
// Try adding an entry
|
||||
fh.addEntry("name-A", "value-A");
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
|
||||
yield updateEntry("add", "name-A", "value-A", next_test);
|
||||
yield countEntries(null, null, checkOne);
|
||||
yield countEntries("name-A", "value-A", checkOne);
|
||||
|
||||
// ===== 4 =====
|
||||
testnum++;
|
||||
// Try removing an entry
|
||||
fh.removeEntry("name-A", "value-A");
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
yield updateEntry("remove", "name-A", "value-A", next_test);
|
||||
yield countEntries(null, null, checkZero);
|
||||
yield countEntries("name-A", "value-A", checkZero);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
@ -1,164 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh, prefs;
|
||||
|
||||
function countAllEntries() {
|
||||
let stmt = fh.DBConnection.createStatement("SELECT COUNT(*) as numEntries FROM moz_formhistory");
|
||||
do_check_true(stmt.executeStep());
|
||||
let numEntries = stmt.row.numEntries;
|
||||
stmt.finalize();
|
||||
return numEntries;
|
||||
}
|
||||
|
||||
function triggerExpiration() {
|
||||
// We can't easily fake a "daily idle" event, so for testing purposes form
|
||||
// history listens for another notification to trigger an immediate
|
||||
// expiration.
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "formhistory-expire-now", null);
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
var testfile = do_get_file("formhistory_expire.sqlite");
|
||||
var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Cleanup from any previous tests or failures.
|
||||
var dbFile = profileDir.clone();
|
||||
dbFile.append("formhistory.sqlite");
|
||||
if (dbFile.exists())
|
||||
dbFile.remove(false);
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
do_check_true(dbFile.exists());
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
prefs = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
|
||||
// We're going to clear this at the end, so it better have the default value now.
|
||||
do_check_false(prefs.prefHasUserValue("browser.formfill.expire_days"));
|
||||
|
||||
|
||||
// ===== 1 =====
|
||||
testnum++;
|
||||
|
||||
// Sanity check initial state
|
||||
do_check_eq(CURRENT_SCHEMA, fh.DBConnection.schemaVersion);
|
||||
do_check_eq(508, countAllEntries());
|
||||
do_check_true(fh.entryExists("name-A", "value-A")); // lastUsed == distant past
|
||||
do_check_true(fh.entryExists("name-B", "value-B")); // lastUsed == distant future
|
||||
|
||||
// Add a new entry
|
||||
do_check_false(fh.entryExists("name-C", "value-C"));
|
||||
fh.addEntry("name-C", "value-C");
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
|
||||
// Check the original db size.
|
||||
// Do a vacuum to make sure the db has current page size.
|
||||
fh.DBConnection.executeSimpleSQL("VACUUM");
|
||||
var oldSize = dbFile.clone().fileSize;
|
||||
|
||||
// Update some existing entries to have ages relative to when the test runs.
|
||||
var now = 1000 * Date.now();
|
||||
var age181 = now - 181 * 24 * PR_HOURS;
|
||||
var age179 = now - 179 * 24 * PR_HOURS;
|
||||
var age31 = now - 31 * 24 * PR_HOURS;
|
||||
var age29 = now - 29 * 24 * PR_HOURS;
|
||||
var age11 = now - 11 * 24 * PR_HOURS;
|
||||
var age9 = now - 9 * 24 * PR_HOURS;
|
||||
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age181 + " WHERE lastUsed=181");
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age179 + " WHERE lastUsed=179");
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age31 + " WHERE lastUsed=31");
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age29 + " WHERE lastUsed=29");
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age11 + " WHERE lastUsed=9999");
|
||||
fh.DBConnection.executeSimpleSQL("UPDATE moz_formhistory SET lastUsed=" + age9 + " WHERE lastUsed=9");
|
||||
|
||||
|
||||
// ===== 2 =====
|
||||
testnum++;
|
||||
|
||||
// Expire history with default pref (180 days)
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("181DaysOld", "foo"));
|
||||
do_check_true(fh.entryExists("179DaysOld", "foo"));
|
||||
do_check_eq(509, countAllEntries());
|
||||
|
||||
// 2 entries are expected to expire.
|
||||
triggerExpiration();
|
||||
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
do_check_false(fh.entryExists("181DaysOld", "foo"));
|
||||
do_check_true(fh.entryExists("179DaysOld", "foo"));
|
||||
do_check_eq(507, countAllEntries());
|
||||
|
||||
|
||||
// ===== 3 =====
|
||||
testnum++;
|
||||
|
||||
// And again. No change expected.
|
||||
triggerExpiration();
|
||||
do_check_eq(507, countAllEntries());
|
||||
|
||||
|
||||
// ===== 4 =====
|
||||
testnum++;
|
||||
|
||||
// Set formfill pref to 30 days.
|
||||
prefs.setIntPref("browser.formfill.expire_days", 30);
|
||||
do_check_true(fh.entryExists("179DaysOld", "foo"));
|
||||
do_check_true(fh.entryExists("bar", "31days"));
|
||||
do_check_true(fh.entryExists("bar", "29days"));
|
||||
do_check_eq(507, countAllEntries());
|
||||
|
||||
triggerExpiration();
|
||||
|
||||
do_check_false(fh.entryExists("179DaysOld", "foo"));
|
||||
do_check_false(fh.entryExists("bar", "31days"));
|
||||
do_check_true(fh.entryExists("bar", "29days"));
|
||||
do_check_eq(505, countAllEntries());
|
||||
|
||||
|
||||
// ===== 5 =====
|
||||
testnum++;
|
||||
|
||||
// Set override pref to 10 days and expire. This expires a large batch of
|
||||
// entries, and should trigger a VACCUM to reduce file size.
|
||||
prefs.setIntPref("browser.formfill.expire_days", 10);
|
||||
|
||||
do_check_true(fh.entryExists("bar", "29days"));
|
||||
do_check_true(fh.entryExists("9DaysOld", "foo"));
|
||||
do_check_eq(505, countAllEntries());
|
||||
|
||||
triggerExpiration();
|
||||
|
||||
do_check_false(fh.entryExists("bar", "29days"));
|
||||
do_check_true(fh.entryExists("9DaysOld", "foo"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
do_check_eq(3, countAllEntries());
|
||||
|
||||
// Check that the file size was reduced.
|
||||
// Need to clone the nsIFile because the size is being cached on Windows.
|
||||
dbFile = dbFile.clone();
|
||||
do_check_true(dbFile.fileSize < oldSize);
|
||||
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
} finally {
|
||||
// Make sure we always reset prefs.
|
||||
if (prefs.prefHasUserValue("browser.formfill.expire_days"))
|
||||
prefs.clearUserPref("browser.formfill.expire_days");
|
||||
}
|
||||
}
|
@ -3,10 +3,101 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var testnum = 0;
|
||||
var fh;
|
||||
let dbConnection; // used for deleted table tests
|
||||
|
||||
function run_test()
|
||||
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
|
||||
function countDeletedEntries(expected)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
let stmt = dbConnection.createAsyncStatement("SELECT COUNT(*) AS numEntries FROM moz_deleted_formhistory");
|
||||
stmt.executeAsync({
|
||||
handleResult: function(resultSet) {
|
||||
do_check_eq(expected, resultSet.getNextRow().getResultByName("numEntries"));
|
||||
deferred.resolve();
|
||||
},
|
||||
handleError : function () {
|
||||
do_throw("Error occurred counting deleted entries: " + error);
|
||||
deferred.reject();
|
||||
},
|
||||
handleCompletion : function () {
|
||||
stmt.finalize();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function checkTimeDeleted(guid, checkFunction)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
let stmt = dbConnection.createAsyncStatement("SELECT timeDeleted FROM moz_deleted_formhistory WHERE guid = :guid");
|
||||
stmt.params.guid = guid;
|
||||
stmt.executeAsync({
|
||||
handleResult: function(resultSet) {
|
||||
checkFunction(resultSet.getNextRow().getResultByName("timeDeleted"));
|
||||
deferred.resolve();
|
||||
},
|
||||
handleError : function () {
|
||||
do_throw("Error occurred getting deleted entries: " + error);
|
||||
deferred.reject();
|
||||
},
|
||||
handleCompletion : function () {
|
||||
stmt.finalize();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseUpdateEntry(op, name, value)
|
||||
{
|
||||
var change = { op: op };
|
||||
if (name !== null)
|
||||
change.fieldname = name;
|
||||
if (value !== null)
|
||||
change.value = value;
|
||||
return promiseUpdate(change);
|
||||
}
|
||||
|
||||
function promiseUpdate(change)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
FormHistory.update(change,
|
||||
{ handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseSearchEntries(terms, params)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
let results = [];
|
||||
FormHistory.search(terms, params,
|
||||
{ handleResult: function(result) results.push(result),
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) deferred.resolve(results); }
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseCountEntries(name, value, checkFn)
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
countEntries(name, value, function (result) { checkFn(result); deferred.resolve(); } );
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function ()
|
||||
{
|
||||
let oldSupportsDeletedTable = FormHistory._supportsDeletedTable;
|
||||
FormHistory._supportsDeletedTable = true;
|
||||
|
||||
try {
|
||||
|
||||
// ===== test init =====
|
||||
@ -21,105 +112,286 @@ function run_test()
|
||||
|
||||
testfile.copyTo(profileDir, "formhistory.sqlite");
|
||||
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
function checkExists(num) { do_check_true(num > 0); }
|
||||
function checkNotExists(num) { do_check_true(num == 0); }
|
||||
|
||||
// ===== 1 =====
|
||||
// Check initial state is as expected
|
||||
testnum++;
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_true(fh.nameExists("name-A"));
|
||||
do_check_true(fh.nameExists("name-B"));
|
||||
do_check_true(fh.nameExists("name-C"));
|
||||
do_check_true(fh.nameExists("name-D"));
|
||||
do_check_true(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B1"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B2"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
yield promiseCountEntries("name-A", null, checkExists);
|
||||
yield promiseCountEntries("name-B", null, checkExists);
|
||||
yield promiseCountEntries("name-C", null, checkExists);
|
||||
yield promiseCountEntries("name-D", null, checkExists);
|
||||
yield promiseCountEntries("name-A", "value-A", checkExists);
|
||||
yield promiseCountEntries("name-B", "value-B1", checkExists);
|
||||
yield promiseCountEntries("name-B", "value-B2", checkExists);
|
||||
yield promiseCountEntries("name-C", "value-C", checkExists);
|
||||
yield promiseCountEntries("name-D", "value-D", checkExists);
|
||||
// time-A/B/C/D checked below.
|
||||
|
||||
// Delete anything from the deleted table
|
||||
let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
|
||||
dbFile.append("formhistory.sqlite");
|
||||
dbConnection = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let stmt = dbConnection.createAsyncStatement("DELETE FROM moz_deleted_formhistory");
|
||||
stmt.executeAsync({
|
||||
handleResult: function(resultSet) { },
|
||||
handleError : function () {
|
||||
do_throw("Error occurred counting deleted all entries: " + error);
|
||||
},
|
||||
handleCompletion : function () {
|
||||
stmt.finalize();
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
// ===== 2 =====
|
||||
// Test looking for nonexistent / bogus data.
|
||||
testnum++;
|
||||
do_check_false(fh.nameExists("blah"));
|
||||
do_check_false(fh.nameExists(""));
|
||||
do_check_false(fh.nameExists(null));
|
||||
do_check_false(fh.entryExists("name-A", "blah"));
|
||||
do_check_false(fh.entryExists("name-A", ""));
|
||||
do_check_false(fh.entryExists("name-A", null));
|
||||
do_check_false(fh.entryExists("blah", "value-A"));
|
||||
do_check_false(fh.entryExists("", "value-A"));
|
||||
do_check_false(fh.entryExists(null, "value-A"));
|
||||
yield promiseCountEntries("blah", null, checkNotExists);
|
||||
yield promiseCountEntries("", null, checkNotExists);
|
||||
yield promiseCountEntries("name-A", "blah", checkNotExists);
|
||||
yield promiseCountEntries("name-A", "", checkNotExists);
|
||||
yield promiseCountEntries("name-A", null, checkExists);
|
||||
yield promiseCountEntries("blah", "value-A", checkNotExists);
|
||||
yield promiseCountEntries("", "value-A", checkNotExists);
|
||||
yield promiseCountEntries(null, "value-A", checkExists);
|
||||
|
||||
// Cannot use promiseCountEntries when name and value are null because it treats null values as not set
|
||||
// and here a search should be done explicity for null.
|
||||
deferred = Promise.defer();
|
||||
yield FormHistory.count({ fieldname: null, value: null },
|
||||
{ handleResult: function(result) checkNotExists(result),
|
||||
handleError: function (error) {
|
||||
do_throw("Error occurred searching form history: " + error);
|
||||
},
|
||||
handleCompletion: function(reason) { if (!reason) deferred.resolve() }
|
||||
});
|
||||
yield deferred.promise;
|
||||
|
||||
// ===== 3 =====
|
||||
// Test removeEntriesForName with a single matching value
|
||||
testnum++;
|
||||
fh.removeEntriesForName("name-A");
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B1"));
|
||||
do_check_true(fh.entryExists("name-B", "value-B2"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
yield promiseUpdateEntry("remove", "name-A", null);
|
||||
|
||||
yield promiseCountEntries("name-A", "value-A", checkNotExists);
|
||||
yield promiseCountEntries("name-B", "value-B1", checkExists);
|
||||
yield promiseCountEntries("name-B", "value-B2", checkExists);
|
||||
yield promiseCountEntries("name-C", "value-C", checkExists);
|
||||
yield promiseCountEntries("name-D", "value-D", checkExists);
|
||||
yield countDeletedEntries(1);
|
||||
|
||||
// ===== 4 =====
|
||||
// Test removeEntriesForName with multiple matching values
|
||||
testnum++;
|
||||
fh.removeEntriesForName("name-B");
|
||||
do_check_false(fh.entryExists("name-A", "value-A"));
|
||||
do_check_false(fh.entryExists("name-B", "value-B1"));
|
||||
do_check_false(fh.entryExists("name-B", "value-B2"));
|
||||
do_check_true(fh.entryExists("name-C", "value-C"));
|
||||
do_check_true(fh.entryExists("name-D", "value-D"));
|
||||
yield promiseUpdateEntry("remove", "name-B", null);
|
||||
|
||||
yield promiseCountEntries("name-A", "value-A", checkNotExists);
|
||||
yield promiseCountEntries("name-B", "value-B1", checkNotExists);
|
||||
yield promiseCountEntries("name-B", "value-B2", checkNotExists);
|
||||
yield promiseCountEntries("name-C", "value-C", checkExists);
|
||||
yield promiseCountEntries("name-D", "value-D", checkExists);
|
||||
yield countDeletedEntries(3);
|
||||
|
||||
// ===== 5 =====
|
||||
// Test removing by time range (single entry, not surrounding entries)
|
||||
testnum++;
|
||||
do_check_true(fh.nameExists("time-A")); // firstUsed=1000, lastUsed=1000
|
||||
do_check_true(fh.nameExists("time-B")); // firstUsed=1000, lastUsed=1099
|
||||
do_check_true(fh.nameExists("time-C")); // firstUsed=1099, lastUsed=1099
|
||||
do_check_true(fh.nameExists("time-D")); // firstUsed=2001, lastUsed=2001
|
||||
fh.removeEntriesByTimeframe(1050, 2000);
|
||||
do_check_true(fh.nameExists("time-A"));
|
||||
do_check_true(fh.nameExists("time-B"));
|
||||
do_check_false(fh.nameExists("time-C"));
|
||||
do_check_true(fh.nameExists("time-D"));
|
||||
yield promiseCountEntries("time-A", null, checkExists); // firstUsed=1000, lastUsed=1000
|
||||
yield promiseCountEntries("time-B", null, checkExists); // firstUsed=1000, lastUsed=1099
|
||||
yield promiseCountEntries("time-C", null, checkExists); // firstUsed=1099, lastUsed=1099
|
||||
yield promiseCountEntries("time-D", null, checkExists); // firstUsed=2001, lastUsed=2001
|
||||
yield promiseUpdate({ op : "remove", firstUsedStart: 1050, firstUsedEnd: 2000 });
|
||||
|
||||
yield promiseCountEntries("time-A", null, checkExists);
|
||||
yield promiseCountEntries("time-B", null, checkExists);
|
||||
yield promiseCountEntries("time-C", null, checkNotExists);
|
||||
yield promiseCountEntries("time-D", null, checkExists);
|
||||
yield countDeletedEntries(4);
|
||||
|
||||
// ===== 6 =====
|
||||
// Test removing by time range (multiple entries)
|
||||
testnum++;
|
||||
fh.removeEntriesByTimeframe(1000, 2000);
|
||||
do_check_false(fh.nameExists("time-A"));
|
||||
do_check_false(fh.nameExists("time-B"));
|
||||
do_check_false(fh.nameExists("time-C"));
|
||||
do_check_true(fh.nameExists("time-D"));
|
||||
yield promiseUpdate({ op : "remove", firstUsedStart: 1000, firstUsedEnd: 2000 });
|
||||
|
||||
yield promiseCountEntries("time-A", null, checkNotExists);
|
||||
yield promiseCountEntries("time-B", null, checkNotExists);
|
||||
yield promiseCountEntries("time-C", null, checkNotExists);
|
||||
yield promiseCountEntries("time-D", null, checkExists);
|
||||
yield countDeletedEntries(6);
|
||||
|
||||
// ===== 7 =====
|
||||
// test removeAllEntries
|
||||
testnum++;
|
||||
fh.removeAllEntries();
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.nameExists("name-C"));
|
||||
do_check_false(fh.nameExists("name-D"));
|
||||
do_check_false(fh.entryExists("name-C", "value-C"));
|
||||
do_check_false(fh.entryExists("name-D", "value-D"));
|
||||
yield promiseUpdateEntry("remove", null, null);
|
||||
|
||||
yield promiseCountEntries("name-C", null, checkNotExists);
|
||||
yield promiseCountEntries("name-D", null, checkNotExists);
|
||||
yield promiseCountEntries("name-C", "value-C", checkNotExists);
|
||||
yield promiseCountEntries("name-D", "value-D", checkNotExists);
|
||||
|
||||
yield promiseCountEntries(null, null, checkNotExists);
|
||||
yield countDeletedEntries(6);
|
||||
|
||||
// ===== 8 =====
|
||||
// Add a single entry back
|
||||
testnum++;
|
||||
fh.addEntry("newname-A", "newvalue-A");
|
||||
do_check_true(fh.hasEntries);
|
||||
do_check_true(fh.entryExists("newname-A", "newvalue-A"));
|
||||
yield promiseUpdateEntry("add", "newname-A", "newvalue-A");
|
||||
yield promiseCountEntries("newname-A", "newvalue-A", checkExists);
|
||||
|
||||
// ===== 9 =====
|
||||
// Remove the single entry
|
||||
testnum++;
|
||||
fh.removeEntry("newname-A", "newvalue-A");
|
||||
do_check_false(fh.hasEntries);
|
||||
do_check_false(fh.entryExists("newname-A", "newvalue-A"));
|
||||
yield promiseUpdateEntry("remove", "newname-A", "newvalue-A");
|
||||
yield promiseCountEntries("newname-A", "newvalue-A", checkNotExists);
|
||||
|
||||
// ===== 10 =====
|
||||
// Add a single entry
|
||||
testnum++;
|
||||
yield promiseUpdateEntry("add", "field1", "value1");
|
||||
yield promiseCountEntries("field1", "value1", checkExists);
|
||||
|
||||
let processFirstResult = function processResults(results)
|
||||
{
|
||||
// Only handle the first result
|
||||
if (results.length > 0) {
|
||||
let result = results[0];
|
||||
return [result.timesUsed, result.firstUsed, result.lastUsed, result.guid];
|
||||
}
|
||||
}
|
||||
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field1", value: "value1" });
|
||||
let [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
do_check_eq(1, timesUsed);
|
||||
do_check_true(firstUsed > 0);
|
||||
do_check_true(lastUsed > 0);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 1));
|
||||
|
||||
// ===== 11 =====
|
||||
// Add another single entry
|
||||
testnum++;
|
||||
yield promiseUpdateEntry("add", "field1", "value1b");
|
||||
yield promiseCountEntries("field1", "value1", checkExists);
|
||||
yield promiseCountEntries("field1", "value1b", checkExists);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2));
|
||||
|
||||
// ===== 12 =====
|
||||
// Update a single entry
|
||||
testnum++;
|
||||
|
||||
results = yield promiseSearchEntries(["guid"], { fieldname: "field1", value: "value1" });
|
||||
let guid = processFirstResult(results)[3];
|
||||
|
||||
yield promiseUpdate({ op : "update", guid: guid, value: "modifiedValue" });
|
||||
yield promiseCountEntries("field1", "modifiedValue", checkExists);
|
||||
yield promiseCountEntries("field1", "value1", checkNotExists);
|
||||
yield promiseCountEntries("field1", "value1b", checkExists);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2));
|
||||
|
||||
// ===== 13 =====
|
||||
// Add a single entry with times
|
||||
testnum++;
|
||||
yield promiseUpdate({ op : "add", fieldname: "field2", value: "value2",
|
||||
timesUsed: 20, firstUsed: 100, lastUsed: 500 });
|
||||
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field2", value: "value2" });
|
||||
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
|
||||
do_check_eq(20, timesUsed);
|
||||
do_check_eq(100, firstUsed);
|
||||
do_check_eq(500, lastUsed);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3));
|
||||
|
||||
// ===== 14 =====
|
||||
// Bump an entry, which updates its lastUsed field
|
||||
testnum++;
|
||||
yield promiseUpdate({ op : "bump", fieldname: "field2", value: "value2",
|
||||
timesUsed: 20, firstUsed: 100, lastUsed: 500 });
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field2", value: "value2" });
|
||||
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
do_check_eq(21, timesUsed);
|
||||
do_check_eq(100, firstUsed);
|
||||
do_check_true(lastUsed > 500);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3));
|
||||
|
||||
// ===== 15 =====
|
||||
// Bump an entry that does not exist
|
||||
testnum++;
|
||||
yield promiseUpdate({ op : "bump", fieldname: "field3", value: "value3",
|
||||
timesUsed: 10, firstUsed: 50, lastUsed: 400 });
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field3", value: "value3" });
|
||||
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
do_check_eq(10, timesUsed);
|
||||
do_check_eq(50, firstUsed);
|
||||
do_check_eq(400, lastUsed);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4));
|
||||
|
||||
// ===== 16 =====
|
||||
// Bump an entry with a guid
|
||||
testnum++;
|
||||
results = yield promiseSearchEntries(["guid"], { fieldname: "field3", value: "value3" });
|
||||
guid = processFirstResult(results)[3];
|
||||
yield promiseUpdate({ op : "bump", guid: guid, timesUsed: 20, firstUsed: 55, lastUsed: 400 });
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field3", value: "value3" });
|
||||
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
do_check_eq(11, timesUsed);
|
||||
do_check_eq(50, firstUsed);
|
||||
do_check_true(lastUsed > 400);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4));
|
||||
|
||||
// ===== 17 =====
|
||||
// Remove an entry
|
||||
testnum++;
|
||||
yield countDeletedEntries(7);
|
||||
|
||||
results = yield promiseSearchEntries(["guid"], { fieldname: "field1", value: "value1b" });
|
||||
guid = processFirstResult(results)[3];
|
||||
|
||||
yield promiseUpdate({ op : "remove", guid: guid});
|
||||
yield promiseCountEntries("field1", "modifiedValue", checkExists);
|
||||
yield promiseCountEntries("field1", "value1b", checkNotExists);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 3));
|
||||
|
||||
yield countDeletedEntries(8);
|
||||
yield checkTimeDeleted(guid, function (timeDeleted) do_check_true(timeDeleted > 10000));
|
||||
|
||||
// ===== 18 =====
|
||||
// Add yet another single entry
|
||||
testnum++;
|
||||
yield promiseUpdate({ op : "add", fieldname: "field4", value: "value4",
|
||||
timesUsed: 5, firstUsed: 230, lastUsed: 600 });
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 4));
|
||||
|
||||
// ===== 19 =====
|
||||
// Remove an entry by time
|
||||
testnum++;
|
||||
results = yield promiseSearchEntries(["timesUsed", "firstUsed", "lastUsed"],
|
||||
{ fieldname: "field1", value: "modifiedValue" });
|
||||
[timesUsed, firstUsed, lastUsed] = processFirstResult(results);
|
||||
|
||||
yield promiseUpdate({ op : "remove", firstUsedStart: 60, firstUsedEnd: 250 });
|
||||
yield promiseCountEntries("field1", "modifiedValue", checkExists);
|
||||
yield promiseCountEntries("field2", "value2", checkNotExists);
|
||||
yield promiseCountEntries("field3", "value3", checkExists);
|
||||
yield promiseCountEntries("field4", "value4", checkNotExists);
|
||||
yield promiseCountEntries(null, null, function(num) do_check_eq(num, 2));
|
||||
yield countDeletedEntries(10);
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + e;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
FormHistory._supportsDeletedTable = oldSupportsDeletedTable;
|
||||
dbConnection.asyncClose(do_test_finished);
|
||||
}
|
||||
});
|
||||
|
||||
function run_test() run_next_test();
|
||||
|
@ -5,10 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var expectedNotification;
|
||||
var expectedBeforeNotification = null;
|
||||
var expectedData;
|
||||
|
||||
var TestObserver = {
|
||||
@ -16,197 +13,146 @@ var TestObserver = {
|
||||
|
||||
observe : function (subject, topic, data) {
|
||||
do_check_eq(topic, "satchel-storage-changed");
|
||||
|
||||
// ensure that the "before-" notification comes before the other
|
||||
dump(expectedBeforeNotification + " : " + expectedNotification + "\n");
|
||||
if (!expectedBeforeNotification)
|
||||
do_check_eq(data, expectedNotification);
|
||||
else
|
||||
do_check_eq(data, expectedBeforeNotification);
|
||||
do_check_eq(data, expectedNotification);
|
||||
|
||||
switch (data) {
|
||||
case "addEntry":
|
||||
do_check_true(subject instanceof Ci.nsIMutableArray);
|
||||
do_check_eq(expectedData[0], subject.queryElementAt(0, Ci.nsISupportsString));
|
||||
do_check_eq(expectedData[1], subject.queryElementAt(1, Ci.nsISupportsString));
|
||||
do_check_true(isGUID.test(subject.queryElementAt(2, Ci.nsISupportsString).toString()));
|
||||
break;
|
||||
case "modifyEntry":
|
||||
do_check_true(subject instanceof Ci.nsIMutableArray);
|
||||
do_check_eq(expectedData[0], subject.queryElementAt(0, Ci.nsISupportsString));
|
||||
do_check_eq(expectedData[1], subject.queryElementAt(1, Ci.nsISupportsString));
|
||||
do_check_true(isGUID.test(subject.queryElementAt(2, Ci.nsISupportsString).toString()));
|
||||
break;
|
||||
case "before-removeEntry":
|
||||
case "removeEntry":
|
||||
do_check_true(subject instanceof Ci.nsIMutableArray);
|
||||
do_check_eq(expectedData[0], subject.queryElementAt(0, Ci.nsISupportsString));
|
||||
do_check_eq(expectedData[1], subject.queryElementAt(1, Ci.nsISupportsString));
|
||||
do_check_true(isGUID.test(subject.queryElementAt(2, Ci.nsISupportsString).toString()));
|
||||
break;
|
||||
case "before-removeAllEntries":
|
||||
case "removeAllEntries":
|
||||
do_check_eq(subject, expectedData);
|
||||
break;
|
||||
case "before-removeEntriesForName":
|
||||
case "removeEntriesForName":
|
||||
case "formhistory-add":
|
||||
case "formhistory-update":
|
||||
do_check_true(subject instanceof Ci.nsISupportsString);
|
||||
do_check_eq(subject, expectedData);
|
||||
do_check_true(isGUID.test(subject.toString()));
|
||||
break;
|
||||
case "before-removeEntriesByTimeframe":
|
||||
case "removeEntriesByTimeframe":
|
||||
do_check_true(subject instanceof Ci.nsIMutableArray);
|
||||
do_check_eq(expectedData[0], subject.queryElementAt(0, Ci.nsISupportsPRInt64));
|
||||
do_check_eq(expectedData[1], subject.queryElementAt(1, Ci.nsISupportsPRInt64));
|
||||
break;
|
||||
case "before-expireOldEntries":
|
||||
case "expireOldEntries":
|
||||
do_check_true(subject instanceof Ci.nsISupportsPRInt64);
|
||||
do_check_true(subject.data > 0);
|
||||
case "formhistory-remove":
|
||||
do_check_eq(null, subject);
|
||||
break;
|
||||
default:
|
||||
do_throw("Unhandled notification: " + data + " / " + topic);
|
||||
}
|
||||
// ensure a duplicate is flagged as unexpected
|
||||
if (expectedBeforeNotification) {
|
||||
expectedBeforeNotification = null;
|
||||
} else {
|
||||
expectedNotification = null;
|
||||
expectedData = null;
|
||||
}
|
||||
|
||||
expectedNotification = null;
|
||||
expectedData = null;
|
||||
}
|
||||
};
|
||||
|
||||
function countAllEntries() {
|
||||
let stmt = fh.DBConnection.createStatement("SELECT COUNT(*) as numEntries FROM moz_formhistory");
|
||||
do_check_true(stmt.step());
|
||||
let numEntries = stmt.row.numEntries;
|
||||
stmt.finalize();
|
||||
return numEntries;
|
||||
}
|
||||
|
||||
function triggerExpiration() {
|
||||
// We can't easily fake a "daily idle" event, so for testing purposes form
|
||||
// history listens for another notification to trigger an immediate
|
||||
// expiration.
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(null, "formhistory-expire-now", null);
|
||||
}
|
||||
let testIterator = null;
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
testIterator = run_test_steps();
|
||||
testIterator.next();
|
||||
}
|
||||
|
||||
function next_test()
|
||||
{
|
||||
testIterator.next();
|
||||
}
|
||||
|
||||
function run_test_steps() {
|
||||
|
||||
try {
|
||||
|
||||
var testnum = 0;
|
||||
var testdesc = "Setup of test form history entries";
|
||||
fh = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
|
||||
do_check_true(fh != null);
|
||||
|
||||
var entry1 = ["entry1", "value1"];
|
||||
var entry2 = ["entry2", "value2"];
|
||||
|
||||
|
||||
// Add the observer
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.addObserver(TestObserver, "satchel-storage-changed", false);
|
||||
|
||||
|
||||
/* ========== 1 ========== */
|
||||
var testnum = 1;
|
||||
var testdesc = "Initial connection to storage module"
|
||||
|
||||
fh.DBConnection.executeSimpleSQL("DELETE FROM moz_formhistory");
|
||||
do_check_eq(countAllEntries(), 0, "Checking initial DB is empty");
|
||||
yield updateEntry("remove", null, null, next_test);
|
||||
yield countEntries(null, null, function (num) { do_check_false(num, "Checking initial DB is empty"); next_test(); });
|
||||
|
||||
// Add the observer
|
||||
var os = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
os.addObserver(TestObserver, "satchel-storage-changed", false);
|
||||
|
||||
/* ========== 2 ========== */
|
||||
testnum++;
|
||||
testdesc = "addEntry";
|
||||
|
||||
expectedNotification = "addEntry";
|
||||
expectedNotification = "formhistory-add";
|
||||
expectedData = entry1;
|
||||
fh.addEntry(entry1[0], entry1[1]);
|
||||
do_check_true(fh.entryExists(entry1[0], entry1[1]));
|
||||
|
||||
yield updateEntry("add", entry1[0], entry1[1], next_test);
|
||||
do_check_eq(expectedNotification, null); // check that observer got a notification
|
||||
|
||||
yield countEntries(entry1[0], entry1[1], function (num) { do_check_true(num > 0); next_test(); });
|
||||
|
||||
/* ========== 3 ========== */
|
||||
testnum++;
|
||||
testdesc = "modifyEntry";
|
||||
|
||||
expectedNotification = "modifyEntry";
|
||||
expectedNotification = "formhistory-update";
|
||||
expectedData = entry1;
|
||||
fh.addEntry(entry1[0], entry1[1]); // will update previous entry
|
||||
// will update previous entry
|
||||
yield updateEntry("update", entry1[0], entry1[1], next_test);
|
||||
yield countEntries(entry1[0], entry1[1], function (num) { do_check_true(num > 0); next_test(); });
|
||||
|
||||
do_check_eq(expectedNotification, null);
|
||||
|
||||
/* ========== 4 ========== */
|
||||
testnum++;
|
||||
testdesc = "removeEntry";
|
||||
|
||||
expectedNotification = "removeEntry";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedNotification = "formhistory-remove";
|
||||
expectedData = entry1;
|
||||
fh.removeEntry(entry1[0], entry1[1]);
|
||||
yield updateEntry("remove", entry1[0], entry1[1], next_test);
|
||||
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
do_check_true(!fh.entryExists(entry1[0], entry1[1]));
|
||||
yield countEntries(entry1[0], entry1[1], function(num) { do_check_false(num, "doesn't exist after remove"); next_test(); });
|
||||
|
||||
/* ========== 5 ========== */
|
||||
testnum++;
|
||||
testdesc = "removeAllEntries";
|
||||
|
||||
expectedNotification = "removeAllEntries";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedNotification = "formhistory-remove";
|
||||
expectedData = null; // no data expected
|
||||
fh.removeAllEntries();
|
||||
yield updateEntry("remove", null, null, next_test);
|
||||
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
|
||||
/* ========== 6 ========== */
|
||||
testnum++;
|
||||
testdesc = "removeAllEntries (again)";
|
||||
|
||||
expectedNotification = "removeAllEntries";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedNotification = "formhistory-remove";
|
||||
expectedData = null;
|
||||
fh.removeAllEntries();
|
||||
yield updateEntry("remove", null, null, next_test);
|
||||
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
|
||||
/* ========== 7 ========== */
|
||||
testnum++;
|
||||
testdesc = "removeEntriesForName";
|
||||
|
||||
expectedNotification = "removeEntriesForName";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedNotification = "formhistory-remove";
|
||||
expectedData = "field2";
|
||||
fh.removeEntriesForName("field2");
|
||||
yield updateEntry("remove", null, "field2", next_test);
|
||||
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
|
||||
/* ========== 8 ========== */
|
||||
testnum++;
|
||||
testdesc = "removeEntriesByTimeframe";
|
||||
|
||||
expectedNotification = "removeEntriesByTimeframe";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedNotification = "formhistory-remove";
|
||||
expectedData = [10, 99999999999];
|
||||
fh.removeEntriesByTimeframe(expectedData[0], expectedData[1]);
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
|
||||
/* ========== 9 ========== */
|
||||
testnum++;
|
||||
testdesc = "expireOldEntries";
|
||||
yield FormHistory.update({ op: "remove", firstUsedStart: expectedData[0], firstUsedEnd: expectedData[1] },
|
||||
{ handleCompletion: function(reason) { if (!reason) next_test() },
|
||||
handleErrors: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
}
|
||||
});
|
||||
|
||||
expectedNotification = "expireOldEntries";
|
||||
expectedBeforeNotification = "before-" + expectedNotification;
|
||||
expectedData = null; // TestObserver checks expiryDate > 0
|
||||
triggerExpiration();
|
||||
do_check_eq(expectedNotification, null);
|
||||
do_check_eq(expectedBeforeNotification, null);
|
||||
|
||||
os.removeObserver(TestObserver, "satchel-storage-changed", false);
|
||||
|
||||
do_test_finished();
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
|
||||
|
@ -2,18 +2,12 @@
|
||||
head = head_satchel.js
|
||||
tail =
|
||||
|
||||
[test_async_expire.js]
|
||||
[test_autocomplete.js]
|
||||
[test_db_corrupt.js]
|
||||
[test_db_update_v1.js]
|
||||
[test_db_update_v1b.js]
|
||||
[test_db_update_v2.js]
|
||||
[test_db_update_v2b.js]
|
||||
[test_db_update_v3.js]
|
||||
[test_db_update_v3b.js]
|
||||
[test_db_update_v4.js]
|
||||
[test_db_update_v4b.js]
|
||||
[test_db_update_v999a.js]
|
||||
[test_db_update_v999b.js]
|
||||
[test_expire.js]
|
||||
[test_history_api.js]
|
||||
[test_notify.js]
|
||||
|
Loading…
Reference in New Issue
Block a user