mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 876002 - Remove nsIFormHistory2 so no synchronous form history code remains, r=markh.
MozReview-Commit-ID: 4hNAqttRddP --HG-- extra : rebase_source : a206e30f11e8a625c29898f04498e2da628c2b91
This commit is contained in:
parent
4068213baf
commit
8596d20f5f
@ -492,7 +492,6 @@
|
||||
@RESPATH@/components/nsWebHandlerApp.js
|
||||
@RESPATH@/components/satchel.manifest
|
||||
@RESPATH@/components/nsFormAutoComplete.js
|
||||
@RESPATH@/components/nsFormHistory.js
|
||||
@RESPATH@/components/FormHistoryStartup.js
|
||||
@RESPATH@/components/nsInputListAutoComplete.js
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
|
@ -465,7 +465,6 @@
|
||||
@RESPATH@/components/nsWebHandlerApp.js
|
||||
@RESPATH@/components/satchel.manifest
|
||||
@RESPATH@/components/nsFormAutoComplete.js
|
||||
@RESPATH@/components/nsFormHistory.js
|
||||
@RESPATH@/components/FormHistoryStartup.js
|
||||
@RESPATH@/components/nsInputListAutoComplete.js
|
||||
@RESPATH@/components/formautofill.manifest
|
||||
|
@ -30,7 +30,6 @@ public class FormHistoryProvider extends SQLiteBridgeContentProvider {
|
||||
private static final UriMatcher URI_MATCHER;
|
||||
|
||||
|
||||
// This should be kept in sync with the db version in toolkit/components/satchel/nsFormHistory.js
|
||||
private static final int DB_VERSION = 4;
|
||||
private static final String DB_FILENAME = "formhistory.sqlite";
|
||||
private static final String TELEMETRY_TAG = "SQLITEBRIDGE_PROVIDER_FORMS";
|
||||
|
@ -336,7 +336,6 @@
|
||||
@BINPATH@/components/nsWebHandlerApp.js
|
||||
@BINPATH@/components/satchel.manifest
|
||||
@BINPATH@/components/nsFormAutoComplete.js
|
||||
@BINPATH@/components/nsFormHistory.js
|
||||
@BINPATH@/components/FormHistoryStartup.js
|
||||
@BINPATH@/components/nsInputListAutoComplete.js
|
||||
@BINPATH@/components/formautofill.manifest
|
||||
|
@ -2,6 +2,8 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FormHistory.jsm");
|
||||
Cu.import("resource://services-common/async.js");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
|
||||
@ -9,13 +11,28 @@ _("Make sure querySpinningly will synchronously fetch rows for a query asyncly")
|
||||
|
||||
const SQLITE_CONSTRAINT_VIOLATION = 19; // http://www.sqlite.org/c3ref/c_abort.html
|
||||
|
||||
var Svc = {};
|
||||
XPCOMUtils.defineLazyServiceGetter(Svc, "Form",
|
||||
"@mozilla.org/satchel/form-history;1",
|
||||
"nsIFormHistory2");
|
||||
// This test is a bit hacky - it was originally written to use the
|
||||
// formhistory.sqlite database using the nsIFormHistory2 sync APIs. However,
|
||||
// that's now been deprecated in favour of the async FormHistory.jsm.
|
||||
// Rather than re-write the test completely, we cheat - we use FormHistory.jsm
|
||||
// to initialize the database, then we just re-open it for these tests.
|
||||
|
||||
// Init the forms database.
|
||||
FormHistory.schemaVersion;
|
||||
|
||||
// and open the database it just created.
|
||||
let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
|
||||
dbFile.append("formhistory.sqlite");
|
||||
let dbConnection = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
do_register_cleanup(() => {
|
||||
let cb = Async.makeSpinningCallback();
|
||||
dbConnection.asyncClose(cb);
|
||||
cb.wait();
|
||||
});
|
||||
|
||||
function querySpinningly(query, names) {
|
||||
let q = Svc.Form.DBConnection.createStatement(query);
|
||||
let q = dbConnection.createStatement(query);
|
||||
let r = Async.querySpinningly(q, names);
|
||||
q.finalize();
|
||||
return r;
|
||||
@ -84,7 +101,7 @@ function run_test() {
|
||||
|
||||
_("Generate an execution error");
|
||||
let query = "INSERT INTO moz_formhistory (fieldname, value) VALUES ('one', NULL)";
|
||||
let stmt = Svc.Form.DBConnection.createStatement(query);
|
||||
let stmt = dbConnection.createStatement(query);
|
||||
let except;
|
||||
try {
|
||||
Async.querySpinningly(stmt);
|
||||
|
@ -11,7 +11,6 @@ BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
|
||||
XPIDL_SOURCES += [
|
||||
'nsIFormAutoComplete.idl',
|
||||
'nsIFormFillController.idl',
|
||||
'nsIFormHistory.idl',
|
||||
'nsIInputListAutoComplete.idl',
|
||||
]
|
||||
|
||||
@ -28,7 +27,6 @@ LOCAL_INCLUDES += [
|
||||
EXTRA_COMPONENTS += [
|
||||
'FormHistoryStartup.js',
|
||||
'nsFormAutoComplete.js',
|
||||
'nsFormHistory.js',
|
||||
'nsInputListAutoComplete.js',
|
||||
'satchel.manifest',
|
||||
]
|
||||
|
@ -1,894 +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/. */
|
||||
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
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, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
const DB_VERSION = 4;
|
||||
const DAY_IN_MS = 86400000; // 1 day in milliseconds
|
||||
|
||||
function FormHistory() {
|
||||
Deprecated.warning(
|
||||
"nsIFormHistory2 is deprecated and will be removed in a future version",
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=879118");
|
||||
this.init();
|
||||
}
|
||||
|
||||
FormHistory.prototype = {
|
||||
classID : Components.ID("{0c1bb408-71a2-403f-854a-3a0659829ded}"),
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormHistory2,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIMessageListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
]),
|
||||
|
||||
debug : true,
|
||||
enabled : true,
|
||||
|
||||
// The current database schema.
|
||||
dbSchema : {
|
||||
tables : {
|
||||
moz_formhistory: {
|
||||
"id" : "INTEGER PRIMARY KEY",
|
||||
"fieldname" : "TEXT NOT NULL",
|
||||
"value" : "TEXT NOT NULL",
|
||||
"timesUsed" : "INTEGER",
|
||||
"firstUsed" : "INTEGER",
|
||||
"lastUsed" : "INTEGER",
|
||||
"guid" : "TEXT"
|
||||
},
|
||||
moz_deleted_formhistory: {
|
||||
"id" : "INTEGER PRIMARY KEY",
|
||||
"timeDeleted" : "INTEGER",
|
||||
"guid" : "TEXT"
|
||||
}
|
||||
},
|
||||
indices : {
|
||||
moz_formhistory_index : {
|
||||
table : "moz_formhistory",
|
||||
columns : ["fieldname"]
|
||||
},
|
||||
moz_formhistory_lastused_index : {
|
||||
table : "moz_formhistory",
|
||||
columns : ["lastUsed"]
|
||||
},
|
||||
moz_formhistory_guid_index : {
|
||||
table : "moz_formhistory",
|
||||
columns : ["guid"]
|
||||
},
|
||||
}
|
||||
},
|
||||
dbStmts : null, // Database statements for memoization
|
||||
dbFile : null,
|
||||
|
||||
_uuidService: null,
|
||||
get uuidService() {
|
||||
if (!this._uuidService)
|
||||
this._uuidService = Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator);
|
||||
return this._uuidService;
|
||||
},
|
||||
|
||||
log : function log(message) {
|
||||
if (!this.debug)
|
||||
return;
|
||||
dump("FormHistory: " + message + "\n");
|
||||
Services.console.logStringMessage("FormHistory: " + message);
|
||||
},
|
||||
|
||||
|
||||
init : function init() {
|
||||
this.updatePrefs();
|
||||
|
||||
this.dbStmts = {};
|
||||
|
||||
// Add observer
|
||||
Services.obs.addObserver(this, "profile-before-change", true);
|
||||
},
|
||||
|
||||
/* ---- nsIFormHistory2 interfaces ---- */
|
||||
|
||||
|
||||
get hasEntries() {
|
||||
return (this.countAllEntries() > 0);
|
||||
},
|
||||
|
||||
|
||||
addEntry : function addEntry(name, value) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
|
||||
this.log("addEntry for " + name + "=" + value);
|
||||
|
||||
let now = Date.now() * 1000; // microseconds
|
||||
|
||||
let [id, guid] = this.getExistingEntryID(name, value);
|
||||
let stmt;
|
||||
|
||||
if (id != -1) {
|
||||
// Update existing entry.
|
||||
let query = "UPDATE moz_formhistory SET timesUsed = timesUsed + 1, lastUsed = :lastUsed WHERE id = :id";
|
||||
let params = {
|
||||
lastUsed : now,
|
||||
id
|
||||
};
|
||||
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
this.sendStringNotification("modifyEntry", name, value, guid);
|
||||
} catch (e) {
|
||||
this.log("addEntry (modify) failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Add new entry.
|
||||
guid = this.generateGUID();
|
||||
|
||||
let query = "INSERT INTO moz_formhistory (fieldname, value, timesUsed, firstUsed, lastUsed, guid) " +
|
||||
"VALUES (:fieldname, :value, :timesUsed, :firstUsed, :lastUsed, :guid)";
|
||||
let params = {
|
||||
fieldname : name,
|
||||
value,
|
||||
timesUsed : 1,
|
||||
firstUsed : now,
|
||||
lastUsed : now,
|
||||
guid
|
||||
};
|
||||
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
this.sendStringNotification("addEntry", name, value, guid);
|
||||
} catch (e) {
|
||||
this.log("addEntry (create) failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
removeEntry : function removeEntry(name, value) {
|
||||
this.log("removeEntry for " + name + "=" + value);
|
||||
|
||||
let [id, guid] = this.getExistingEntryID(name, value);
|
||||
this.sendStringNotification("before-removeEntry", name, value, guid);
|
||||
|
||||
let stmt;
|
||||
let query = "DELETE FROM moz_formhistory WHERE id = :id";
|
||||
let params = { id };
|
||||
let existingTransactionInProgress;
|
||||
|
||||
try {
|
||||
// Don't start a transaction if one is already in progress since we can't nest them.
|
||||
existingTransactionInProgress = this.dbConnection.transactionInProgress;
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.beginTransaction();
|
||||
this.moveToDeletedTable("VALUES (:guid, :timeDeleted)", {
|
||||
guid,
|
||||
timeDeleted: Date.now()
|
||||
});
|
||||
|
||||
// remove from the formhistory database
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
this.sendStringNotification("removeEntry", name, value, guid);
|
||||
} catch (e) {
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.rollbackTransaction();
|
||||
this.log("removeEntry failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.commitTransaction();
|
||||
},
|
||||
|
||||
|
||||
removeEntriesForName : function removeEntriesForName(name) {
|
||||
this.log("removeEntriesForName with name=" + name);
|
||||
|
||||
this.sendStringNotification("before-removeEntriesForName", name);
|
||||
|
||||
let stmt;
|
||||
let query = "DELETE FROM moz_formhistory WHERE fieldname = :fieldname";
|
||||
let params = { fieldname : name };
|
||||
let existingTransactionInProgress;
|
||||
|
||||
try {
|
||||
// Don't start a transaction if one is already in progress since we can't nest them.
|
||||
existingTransactionInProgress = this.dbConnection.transactionInProgress;
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.beginTransaction();
|
||||
this.moveToDeletedTable(
|
||||
"SELECT guid, :timeDeleted FROM moz_formhistory " +
|
||||
"WHERE fieldname = :fieldname", {
|
||||
fieldname: name,
|
||||
timeDeleted: Date.now()
|
||||
});
|
||||
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
this.sendStringNotification("removeEntriesForName", name);
|
||||
} catch (e) {
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.rollbackTransaction();
|
||||
this.log("removeEntriesForName failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.commitTransaction();
|
||||
},
|
||||
|
||||
|
||||
removeAllEntries : function removeAllEntries() {
|
||||
this.log("removeAllEntries");
|
||||
|
||||
this.sendNotification("before-removeAllEntries", null);
|
||||
|
||||
let stmt;
|
||||
let query = "DELETE FROM moz_formhistory";
|
||||
let existingTransactionInProgress;
|
||||
|
||||
try {
|
||||
// Don't start a transaction if one is already in progress since we can't nest them.
|
||||
existingTransactionInProgress = this.dbConnection.transactionInProgress;
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.beginTransaction();
|
||||
// TODO: Add these items to the deleted items table once we've sorted
|
||||
// out the issues from bug 756701
|
||||
stmt = this.dbCreateStatement(query);
|
||||
stmt.execute();
|
||||
this.sendNotification("removeAllEntries", null);
|
||||
} catch (e) {
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.rollbackTransaction();
|
||||
this.log("removeAllEntries failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.commitTransaction();
|
||||
},
|
||||
|
||||
|
||||
nameExists : function nameExists(name) {
|
||||
this.log("nameExists for name=" + name);
|
||||
let stmt;
|
||||
let query = "SELECT COUNT(1) AS numEntries FROM moz_formhistory WHERE fieldname = :fieldname";
|
||||
let params = { fieldname : name };
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.executeStep();
|
||||
return (stmt.row.numEntries > 0);
|
||||
} catch (e) {
|
||||
this.log("nameExists failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
entryExists : function entryExists(name, value) {
|
||||
this.log("entryExists for " + name + "=" + value);
|
||||
let [id] = this.getExistingEntryID(name, value);
|
||||
this.log("entryExists: id=" + id);
|
||||
return (id != -1);
|
||||
},
|
||||
|
||||
removeEntriesByTimeframe : function removeEntriesByTimeframe(beginTime, endTime) {
|
||||
this.log("removeEntriesByTimeframe for " + beginTime + " to " + endTime);
|
||||
|
||||
this.sendIntNotification("before-removeEntriesByTimeframe", beginTime, endTime);
|
||||
|
||||
let stmt;
|
||||
let query = "DELETE FROM moz_formhistory WHERE firstUsed >= :beginTime AND firstUsed <= :endTime";
|
||||
let params = {
|
||||
beginTime,
|
||||
endTime
|
||||
};
|
||||
let existingTransactionInProgress;
|
||||
|
||||
try {
|
||||
// Don't start a transaction if one is already in progress since we can't nest them.
|
||||
existingTransactionInProgress = this.dbConnection.transactionInProgress;
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.beginTransaction();
|
||||
this.moveToDeletedTable(
|
||||
"SELECT guid, :timeDeleted FROM moz_formhistory " +
|
||||
"WHERE firstUsed >= :beginTime AND firstUsed <= :endTime", {
|
||||
beginTime,
|
||||
endTime
|
||||
});
|
||||
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.executeStep();
|
||||
this.sendIntNotification("removeEntriesByTimeframe", beginTime, endTime);
|
||||
} catch (e) {
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.rollbackTransaction();
|
||||
this.log("removeEntriesByTimeframe failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
if (!existingTransactionInProgress)
|
||||
this.dbConnection.commitTransaction();
|
||||
},
|
||||
|
||||
moveToDeletedTable : function moveToDeletedTable(values, params) {
|
||||
if (AppConstants.platform == "android") {
|
||||
this.log("Moving entries to deleted table.");
|
||||
|
||||
let stmt;
|
||||
|
||||
try {
|
||||
// Move the entries to the deleted items table.
|
||||
let query = "INSERT INTO moz_deleted_formhistory (guid, timeDeleted) ";
|
||||
if (values) query += values;
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
} catch (e) {
|
||||
this.log("Moving deleted entries failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get dbConnection() {
|
||||
// Make sure dbConnection can't be called from now to prevent infinite loops.
|
||||
delete FormHistory.prototype.dbConnection;
|
||||
|
||||
try {
|
||||
this.dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
|
||||
this.dbFile.append("formhistory.sqlite");
|
||||
this.log("Opening database at " + this.dbFile.path);
|
||||
|
||||
FormHistory.prototype.dbConnection = this.dbOpen();
|
||||
this.dbInit();
|
||||
} catch (e) {
|
||||
this.log("Initialization failed: " + e);
|
||||
// If dbInit fails...
|
||||
if (e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
|
||||
this.dbCleanup();
|
||||
FormHistory.prototype.dbConnection = this.dbOpen();
|
||||
this.dbInit();
|
||||
} else {
|
||||
throw "Initialization failed";
|
||||
}
|
||||
}
|
||||
|
||||
return FormHistory.prototype.dbConnection;
|
||||
},
|
||||
|
||||
get DBConnection() {
|
||||
return this.dbConnection;
|
||||
},
|
||||
|
||||
|
||||
/* ---- nsIObserver interface ---- */
|
||||
|
||||
|
||||
observe : function observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "nsPref:changed":
|
||||
this.updatePrefs();
|
||||
break;
|
||||
case "profile-before-change":
|
||||
this._dbClose(false);
|
||||
break;
|
||||
default:
|
||||
this.log("Oops! Unexpected notification: " + topic);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* ---- helpers ---- */
|
||||
|
||||
|
||||
generateGUID() {
|
||||
// string like: "{f60d9eac-9421-4abc-8491-8e8322b063d4}"
|
||||
let uuid = this.uuidService.generateUUID().toString();
|
||||
let raw = ""; // A string with the low bytes set to random values
|
||||
let bytes = 0;
|
||||
for (let i = 1; bytes < 12 ; i += 2) {
|
||||
// Skip dashes
|
||||
if (uuid[i] == "-")
|
||||
i++;
|
||||
let hexVal = parseInt(uuid[i] + uuid[i + 1], 16);
|
||||
raw += String.fromCharCode(hexVal);
|
||||
bytes++;
|
||||
}
|
||||
return btoa(raw);
|
||||
},
|
||||
|
||||
|
||||
sendStringNotification(changeType, str1, str2, str3) {
|
||||
function wrapit(str) {
|
||||
let wrapper = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
wrapper.data = str;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
let strData;
|
||||
if (arguments.length == 2) {
|
||||
// Just 1 string, no need to put it in an array
|
||||
strData = wrapit(str1);
|
||||
} else {
|
||||
// 3 strings, put them in an array.
|
||||
strData = Cc["@mozilla.org/array;1"].
|
||||
createInstance(Ci.nsIMutableArray);
|
||||
strData.appendElement(wrapit(str1), false);
|
||||
strData.appendElement(wrapit(str2), false);
|
||||
strData.appendElement(wrapit(str3), false);
|
||||
}
|
||||
this.sendNotification(changeType, strData);
|
||||
},
|
||||
|
||||
|
||||
sendIntNotification(changeType, int1, int2) {
|
||||
function wrapit(int) {
|
||||
let wrapper = Cc["@mozilla.org/supports-PRInt64;1"].
|
||||
createInstance(Ci.nsISupportsPRInt64);
|
||||
wrapper.data = int;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
let intData;
|
||||
if (arguments.length == 2) {
|
||||
// Just 1 int, no need for an array
|
||||
intData = wrapit(int1);
|
||||
} else {
|
||||
// 2 ints, put them in an array.
|
||||
intData = Cc["@mozilla.org/array;1"].
|
||||
createInstance(Ci.nsIMutableArray);
|
||||
intData.appendElement(wrapit(int1), false);
|
||||
intData.appendElement(wrapit(int2), false);
|
||||
}
|
||||
this.sendNotification(changeType, intData);
|
||||
},
|
||||
|
||||
|
||||
sendNotification(changeType, data) {
|
||||
Services.obs.notifyObservers(data, "satchel-storage-changed", changeType);
|
||||
},
|
||||
|
||||
|
||||
getExistingEntryID(name, value) {
|
||||
let id = -1, guid = null;
|
||||
let stmt;
|
||||
let query = "SELECT id, guid FROM moz_formhistory WHERE fieldname = :fieldname AND value = :value";
|
||||
let params = {
|
||||
fieldname : name,
|
||||
value
|
||||
};
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
if (stmt.executeStep()) {
|
||||
id = stmt.row.id;
|
||||
guid = stmt.row.guid;
|
||||
}
|
||||
} catch (e) {
|
||||
this.log("getExistingEntryID failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return [id, guid];
|
||||
},
|
||||
|
||||
|
||||
countAllEntries() {
|
||||
let query = "SELECT COUNT(1) AS numEntries FROM moz_formhistory";
|
||||
|
||||
let stmt, numEntries;
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, null);
|
||||
stmt.executeStep();
|
||||
numEntries = stmt.row.numEntries;
|
||||
} catch (e) {
|
||||
this.log("countAllEntries failed: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
this.log("countAllEntries: counted entries: " + numEntries);
|
||||
return numEntries;
|
||||
},
|
||||
|
||||
|
||||
updatePrefs() {
|
||||
this.debug = Services.prefs.getBoolPref("browser.formfill.debug");
|
||||
this.enabled = Services.prefs.getBoolPref("browser.formfill.enable");
|
||||
},
|
||||
|
||||
// Database Creation & Access
|
||||
|
||||
/*
|
||||
* dbCreateStatement
|
||||
*
|
||||
* Creates a statement, wraps it, and then does parameter replacement
|
||||
* Will use memoization so that statements can be reused.
|
||||
*/
|
||||
dbCreateStatement(query, params) {
|
||||
let stmt = this.dbStmts[query];
|
||||
// Memoize the statements
|
||||
if (!stmt) {
|
||||
this.log("Creating new statement for query: " + query);
|
||||
stmt = this.dbConnection.createStatement(query);
|
||||
this.dbStmts[query] = stmt;
|
||||
}
|
||||
// Replace parameters, must be done 1 at a time
|
||||
if (params)
|
||||
for (let i in params)
|
||||
stmt.params[i] = params[i];
|
||||
return stmt;
|
||||
},
|
||||
|
||||
/*
|
||||
* dbOpen
|
||||
*
|
||||
* Open a connection with the database and returns it.
|
||||
*
|
||||
* @returns a db connection object.
|
||||
*/
|
||||
dbOpen() {
|
||||
this.log("Open Database");
|
||||
|
||||
let storage = Cc["@mozilla.org/storage/service;1"].
|
||||
getService(Ci.mozIStorageService);
|
||||
return storage.openDatabase(this.dbFile);
|
||||
},
|
||||
|
||||
/*
|
||||
* dbInit
|
||||
*
|
||||
* Attempts to initialize the database. This creates the file if it doesn't
|
||||
* exist, performs any migrations, etc.
|
||||
*/
|
||||
dbInit() {
|
||||
this.log("Initializing Database");
|
||||
|
||||
let version = this.dbConnection.schemaVersion;
|
||||
|
||||
// Note: Firefox 3 didn't set a schema value, so it started from 0.
|
||||
// So we can't depend on a simple version == 0 check
|
||||
if (version == 0 && !this.dbConnection.tableExists("moz_formhistory"))
|
||||
this.dbCreate();
|
||||
else if (version != DB_VERSION)
|
||||
this.dbMigrate(version);
|
||||
},
|
||||
|
||||
|
||||
dbCreate() {
|
||||
this.log("Creating DB -- tables");
|
||||
for (let name in this.dbSchema.tables) {
|
||||
let table = this.dbSchema.tables[name];
|
||||
this.dbCreateTable(name, table);
|
||||
}
|
||||
|
||||
this.log("Creating DB -- indices");
|
||||
for (let name in this.dbSchema.indices) {
|
||||
let index = this.dbSchema.indices[name];
|
||||
let statement = "CREATE INDEX IF NOT EXISTS " + name + " ON " + index.table +
|
||||
"(" + index.columns.join(", ") + ")";
|
||||
this.dbConnection.executeSimpleSQL(statement);
|
||||
}
|
||||
|
||||
this.dbConnection.schemaVersion = DB_VERSION;
|
||||
},
|
||||
|
||||
dbCreateTable(name, table) {
|
||||
let tSQL = Object.keys(table).map(col => [col, table[col]].join(" ")).join(", ");
|
||||
this.log("Creating table " + name + " with " + tSQL);
|
||||
this.dbConnection.createTable(name, tSQL);
|
||||
},
|
||||
|
||||
dbMigrate(oldVersion) {
|
||||
this.log("Attempting to migrate from version " + oldVersion);
|
||||
|
||||
if (oldVersion > DB_VERSION) {
|
||||
this.log("Downgrading to version " + DB_VERSION);
|
||||
// User's DB is newer. Sanity check that our expected columns are
|
||||
// present, and if so mark the lower version and merrily continue
|
||||
// on. If the columns are borked, something is wrong so blow away
|
||||
// the DB and start from scratch. [Future incompatible upgrades
|
||||
// should swtich to a different table or file.]
|
||||
|
||||
if (!this.dbAreExpectedColumnsPresent())
|
||||
throw Components.Exception("DB is missing expected columns",
|
||||
Cr.NS_ERROR_FILE_CORRUPTED);
|
||||
|
||||
// Change the stored version to the current version. If the user
|
||||
// runs the newer code again, it will see the lower version number
|
||||
// and re-upgrade (to fixup any entries the old code added).
|
||||
this.dbConnection.schemaVersion = DB_VERSION;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade to newer version...
|
||||
|
||||
this.dbConnection.beginTransaction();
|
||||
|
||||
try {
|
||||
for (let v = oldVersion + 1; v <= DB_VERSION; v++) {
|
||||
this.log("Upgrading to version " + v + "...");
|
||||
let migrateFunction = "dbMigrateToVersion" + v;
|
||||
this[migrateFunction]();
|
||||
}
|
||||
} catch (e) {
|
||||
this.log("Migration failed: " + e);
|
||||
this.dbConnection.rollbackTransaction();
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.dbConnection.schemaVersion = DB_VERSION;
|
||||
this.dbConnection.commitTransaction();
|
||||
this.log("DB migration completed.");
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* dbMigrateToVersion1
|
||||
*
|
||||
* Updates the DB schema to v1 (bug 463154).
|
||||
* Adds firstUsed, lastUsed, timesUsed columns.
|
||||
*/
|
||||
dbMigrateToVersion1() {
|
||||
// Check to see if the new columns already exist (could be a v1 DB that
|
||||
// was downgraded to v0). If they exist, we don't need to add them.
|
||||
let query;
|
||||
["timesUsed", "firstUsed", "lastUsed"].forEach(function(column) {
|
||||
if (!this.dbColumnExists(column)) {
|
||||
query = "ALTER TABLE moz_formhistory ADD COLUMN " + column + " INTEGER";
|
||||
this.dbConnection.executeSimpleSQL(query);
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Set the default values for the new columns.
|
||||
//
|
||||
// Note that we set the timestamps to 24 hours in the past. We want a
|
||||
// timestamp that's recent (so that "keep form history for 90 days"
|
||||
// doesn't expire things surprisingly soon), but not so recent that
|
||||
// "forget the last hour of stuff" deletes all freshly migrated data.
|
||||
let stmt;
|
||||
query = "UPDATE moz_formhistory " +
|
||||
"SET timesUsed = 1, firstUsed = :time, lastUsed = :time " +
|
||||
"WHERE timesUsed isnull OR firstUsed isnull or lastUsed isnull";
|
||||
let params = { time: (Date.now() - DAY_IN_MS) * 1000 }
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
} catch (e) {
|
||||
this.log("Failed setting timestamps: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* dbMigrateToVersion2
|
||||
*
|
||||
* Updates the DB schema to v2 (bug 243136).
|
||||
* Adds lastUsed index, removes moz_dummy_table
|
||||
*/
|
||||
dbMigrateToVersion2() {
|
||||
let query = "DROP TABLE IF EXISTS moz_dummy_table";
|
||||
this.dbConnection.executeSimpleSQL(query);
|
||||
|
||||
query = "CREATE INDEX IF NOT EXISTS moz_formhistory_lastused_index ON moz_formhistory (lastUsed)";
|
||||
this.dbConnection.executeSimpleSQL(query);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* dbMigrateToVersion3
|
||||
*
|
||||
* Updates the DB schema to v3 (bug 506402).
|
||||
* Adds guid column and index.
|
||||
*/
|
||||
dbMigrateToVersion3() {
|
||||
// Check to see if GUID column already exists, add if needed
|
||||
let query;
|
||||
if (!this.dbColumnExists("guid")) {
|
||||
query = "ALTER TABLE moz_formhistory ADD COLUMN guid TEXT";
|
||||
this.dbConnection.executeSimpleSQL(query);
|
||||
|
||||
query = "CREATE INDEX IF NOT EXISTS moz_formhistory_guid_index ON moz_formhistory (guid)";
|
||||
this.dbConnection.executeSimpleSQL(query);
|
||||
}
|
||||
|
||||
// Get a list of IDs for existing logins
|
||||
let ids = [];
|
||||
query = "SELECT id FROM moz_formhistory WHERE guid isnull";
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query);
|
||||
while (stmt.executeStep())
|
||||
ids.push(stmt.row.id);
|
||||
} catch (e) {
|
||||
this.log("Failed getting IDs: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a GUID for each login and update the DB.
|
||||
query = "UPDATE moz_formhistory SET guid = :guid WHERE id = :id";
|
||||
for (let id of ids) {
|
||||
let params = {
|
||||
id,
|
||||
guid : this.generateGUID()
|
||||
};
|
||||
|
||||
try {
|
||||
stmt = this.dbCreateStatement(query, params);
|
||||
stmt.execute();
|
||||
} catch (e) {
|
||||
this.log("Failed setting GUID: " + e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (stmt) {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
dbMigrateToVersion4() {
|
||||
if (!this.dbConnection.tableExists("moz_deleted_formhistory")) {
|
||||
this.dbCreateTable("moz_deleted_formhistory", this.dbSchema.tables.moz_deleted_formhistory);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* dbAreExpectedColumnsPresent
|
||||
*
|
||||
* Sanity check to ensure that the columns this version of the code expects
|
||||
* are present in the DB we're using.
|
||||
*/
|
||||
dbAreExpectedColumnsPresent() {
|
||||
for (let name in this.dbSchema.tables) {
|
||||
let table = this.dbSchema.tables[name];
|
||||
let query = "SELECT " +
|
||||
Object.keys(table).join(", ") +
|
||||
" FROM " + name;
|
||||
try {
|
||||
let stmt = this.dbConnection.createStatement(query);
|
||||
// (no need to execute statement, if it compiled we're good)
|
||||
stmt.finalize();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.log("verified that expected columns are present in DB.");
|
||||
return true;
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* dbColumnExists
|
||||
*
|
||||
* Checks to see if the named column already exists.
|
||||
*/
|
||||
dbColumnExists(columnName) {
|
||||
let query = "SELECT " + columnName + " FROM moz_formhistory";
|
||||
try {
|
||||
let stmt = this.dbConnection.createStatement(query);
|
||||
// (no need to execute statement, if it compiled we're good)
|
||||
stmt.finalize();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* _dbClose
|
||||
*
|
||||
* Finalize all statements and close the connection.
|
||||
*
|
||||
* @param aBlocking - Should we spin the loop waiting for the db to be
|
||||
* closed.
|
||||
*/
|
||||
_dbClose : function FH__dbClose(aBlocking) {
|
||||
for (let query in this.dbStmts) {
|
||||
let stmt = this.dbStmts[query];
|
||||
stmt.finalize();
|
||||
}
|
||||
this.dbStmts = {};
|
||||
|
||||
let connectionDescriptor = Object.getOwnPropertyDescriptor(FormHistory.prototype, "dbConnection");
|
||||
// Return if the database hasn't been opened.
|
||||
if (!connectionDescriptor || connectionDescriptor.value === undefined)
|
||||
return;
|
||||
|
||||
let completed = false;
|
||||
try {
|
||||
this.dbConnection.asyncClose(function() { completed = true; });
|
||||
} catch (e) {
|
||||
completed = true;
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (aBlocking && !completed) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* dbCleanup
|
||||
*
|
||||
* Called when database creation fails. Finalizes database statements,
|
||||
* closes the database connection, deletes the database file.
|
||||
*/
|
||||
dbCleanup() {
|
||||
this.log("Cleaning up DB file - close & remove & backup")
|
||||
|
||||
// Create backup file
|
||||
let storage = Cc["@mozilla.org/storage/service;1"].
|
||||
getService(Ci.mozIStorageService);
|
||||
let backupFile = this.dbFile.leafName + ".corrupt";
|
||||
storage.backupDatabaseFile(this.dbFile, backupFile);
|
||||
|
||||
this._dbClose(true);
|
||||
this.dbFile.remove(false);
|
||||
}
|
||||
};
|
||||
|
||||
var component = [FormHistory];
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
|
@ -1,74 +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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
interface nsIFile;
|
||||
interface mozIStorageConnection;
|
||||
|
||||
/**
|
||||
* The nsIFormHistory object is a service which holds a set of name/value
|
||||
* pairs. The names correspond to form field names, and the values correspond
|
||||
* to values the user has submitted. So, several values may exist for a single
|
||||
* name.
|
||||
*
|
||||
* Note: this interface provides no means to access stored values.
|
||||
* Stored values are used by the FormFillController to generate
|
||||
* autocomplete matches.
|
||||
*
|
||||
* @deprecated use FormHistory.jsm instead.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(5d7d84d1-9798-4016-bf61-a32acf09b29d)]
|
||||
interface nsIFormHistory2 : nsISupports
|
||||
{
|
||||
/**
|
||||
* Returns true if the form history has any entries.
|
||||
*/
|
||||
readonly attribute boolean hasEntries;
|
||||
|
||||
/**
|
||||
* Adds a name and value pair to the form history.
|
||||
*/
|
||||
void addEntry(in AString name, in AString value);
|
||||
|
||||
/**
|
||||
* Removes a name and value pair from the form history.
|
||||
*/
|
||||
void removeEntry(in AString name, in AString value);
|
||||
|
||||
/**
|
||||
* Removes all entries that are paired with a name.
|
||||
*/
|
||||
void removeEntriesForName(in AString name);
|
||||
|
||||
/**
|
||||
* Removes all entries in the entire form history.
|
||||
*/
|
||||
void removeAllEntries();
|
||||
|
||||
/**
|
||||
* Returns true if there is no entry that is paired with a name.
|
||||
*/
|
||||
boolean nameExists(in AString name);
|
||||
|
||||
/**
|
||||
* Gets whether a name and value pair exists in the form history.
|
||||
*/
|
||||
boolean entryExists(in AString name, in AString value);
|
||||
|
||||
/**
|
||||
* Removes entries that were created between the specified times.
|
||||
*
|
||||
* @param aBeginTime
|
||||
* The beginning of the timeframe, in microseconds
|
||||
* @param aEndTime
|
||||
* The end of the timeframe, in microseconds
|
||||
*/
|
||||
void removeEntriesByTimeframe(in long long aBeginTime, in long long aEndTime);
|
||||
|
||||
/**
|
||||
* Returns the underlying DB connection the form history module is using.
|
||||
*/
|
||||
readonly attribute mozIStorageConnection DBConnection;
|
||||
};
|
@ -1,5 +1,3 @@
|
||||
component {0c1bb408-71a2-403f-854a-3a0659829ded} nsFormHistory.js
|
||||
contract @mozilla.org/satchel/form-history;1 {0c1bb408-71a2-403f-854a-3a0659829ded}
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user