Bug 439716 - Form Manager should be a JavaScript-based component. p=zpao+dolske, r=zpao, r=dolske, r=gavin

This commit is contained in:
Justin Dolske 2010-05-25 21:35:09 -07:00
Родитель 46686de686
Коммит 80d9553709
7 изменённых файлов: 943 добавлений и 1309 удалений

Просмотреть файл

@ -148,10 +148,6 @@
#define NS_FORMFILLCONTROLLER_CID \
{ 0x895db6c7, 0xdbdf, 0x40ea, { 0x9f, 0x64, 0xb1, 0x75, 0x3, 0x32, 0x43, 0xdc } }
// {A2059C0E-5A58-4c55-AB7C-26F0557546EF}
#define NS_FORMHISTORY_CID \
{ 0xa2059c0e, 0x5a58, 0x4c55, { 0xab, 0x7c, 0x26, 0xf0, 0x55, 0x75, 0x46, 0xef } }
// {59648a91-5a60-4122-8ff2-54b839c84aed}
#define NS_GLOBALHISTORY_CID \
{ 0x59648a91, 0x5a60, 0x4122, { 0x8f, 0xf2, 0x54, 0xb8, 0x39, 0xc8, 0x4a, 0xed} }

Просмотреть файл

@ -51,12 +51,12 @@ LIBXUL_LIBRARY = 1
EXPORT_LIBRARY = 1
EXTRA_COMPONENTS = \
nsFormHistory.js \
nsFormAutoComplete.js \
$(NULL)
CPPSRCS = nsFormFillController.cpp \
nsStorageFormHistory.cpp \
$(NULL)
LOCAL_INCLUDES = \

Просмотреть файл

@ -61,8 +61,8 @@ FormAutoComplete.prototype = {
},
_prefBranch : null,
_debug : false, // mirrors browser.formfill.debug
_enabled : true, // mirrors browser.formfill.enable preference
_debug : true, // mirrors browser.formfill.debug
_enabled : true, // mirrors browser.formfill.enable preference
_agedWeight : 2,
_bucketSize : 1,
_maxTimeGroupings : 25,
@ -133,6 +133,7 @@ FormAutoComplete.prototype = {
}
} else if (topic == "xpcom-shutdown") {
self._dbStmts = null;
self.__formHistory = null;
}
}
},

Просмотреть файл

@ -39,7 +39,6 @@
#include "nsFormFillController.h"
#include "nsStorageFormHistory.h"
#include "nsIFormAutoComplete.h"
#include "nsIAutoCompleteSimpleResult.h"
#include "nsString.h"
@ -1210,16 +1209,10 @@ nsFormFillController::IsEventTrusted(nsIDOMEvent *aEvent)
return isTrusted;
}
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFormHistory, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormFillController)
static const nsModuleComponentInfo components[] =
{
{ "HTML Form History",
NS_FORMHISTORY_CID,
NS_FORMHISTORY_CONTRACTID,
nsFormHistoryConstructor },
{ "HTML Form Fill Controller",
NS_FORMFILLCONTROLLER_CID,
"@mozilla.org/satchel/form-fill-controller;1",

Просмотреть файл

@ -0,0 +1,939 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.com> (original authors)
* Paul OShannessy <paul@oshannessy.com> (original authors)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
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");
const DB_VERSION = 3;
const DAY_IN_MS = 86400000; // 1 day in milliseconds
function FormHistory() {
this.init();
}
FormHistory.prototype = {
classDescription : "FormHistory",
contractID : "@mozilla.org/satchel/form-history;1",
classID : Components.ID("{0c1bb408-71a2-403f-854a-3a0659829ded}"),
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormHistory2, Ci.nsIObserver, Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference]),
debug : true,
enabled : true,
saveHttpsForms : true,
prefBranch : null,
// 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"
},
},
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"]
},
}
},
dbConnection : null, // The database connection
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;
},
// Private Browsing Service
// If the service is not available, null will be returned.
_privBrowsingSvc : undefined,
get privBrowsingSvc() {
if (this._privBrowsingSvc == undefined) {
if ("@mozilla.org/privatebrowsing;1" in Cc)
this._privBrowsingSvc = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
else
this._privBrowsingSvc = null;
}
return this._privBrowsingSvc;
},
log : function (message) {
if (!this.debug)
return;
dump("FormHistory: " + message + "\n");
Services.console.logStringMessage("FormHistory: " + message);
},
init : function() {
let self = this;
this.prefBranch = Services.prefs.getBranch("browser.formfill.");
this.prefBranch.QueryInterface(Ci.nsIPrefBranch2);
this.prefBranch.addObserver("", this, true);
this.updatePrefs();
this.dbStmts = {};
// Add observers
Services.obs.addObserver(this, "earlyformsubmit", false);
Services.obs.addObserver(function() { self.expireOldEntries() }, "idle-daily", false);
Services.obs.addObserver(function() { self.expireOldEntries() }, "formhistory-expire-now", false);
try {
this.dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
this.dbFile.append("formhistory.sqlite");
this.log("Opening database at " + this.dbFile.path);
this.dbInit();
} catch (e) {
this.log("Initialization failed: " + e);
// If dbInit fails...
if (e.result == Cr.NS_ERROR_FILE_CORRUPTED) {
this.dbCleanup(true);
this.dbInit();
} else {
throw "Initialization failed";
}
}
},
/* ---- nsIFormHistory2 interfaces ---- */
get hasEntries() {
return (this.countAllEntries() > 0);
},
addEntry : function (name, value) {
if (!this.enabled ||
this.privBrowsingSvc && this.privBrowsingSvc.privateBrowsingEnabled)
return;
this.log("addEntry for " + name + "=" + value);
let now = Date.now() * 1000; // microseconds
let [id, guid] = this.getExistingEntryID(name, value);
if (id != -1) {
// Update existing entry
let stmt;
let query = "UPDATE moz_formhistory SET timesUsed = timesUsed + 1, lastUsed = :lastUsed WHERE id = :id";
let params = {
lastUsed : now,
id : 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 {
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 : value,
timesUsed : 1,
firstUsed : now,
lastUsed : now,
guid : 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 {
stmt.reset();
}
}
},
removeEntry : function (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 : id };
try {
stmt = this.dbCreateStatement(query, params);
stmt.execute();
this.sendStringNotification("removeEntry", name, value, guid);
} catch (e) {
this.log("removeEntry failed: " + e);
throw e;
} finally {
stmt.reset();
}
},
removeEntriesForName : function (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 };
try {
stmt = this.dbCreateStatement(query, params);
stmt.execute();
this.sendStringNotification("removeEntriesForName", name);
} catch (e) {
this.log("removeEntriesForName failed: " + e);
throw e;
} finally {
stmt.reset();
}
},
removeAllEntries : function () {
this.log("removeAllEntries");
this.sendNotification("before-removeAllEntries", null);
let stmt;
let query = "DELETE FROM moz_formhistory";
try {
stmt = this.dbCreateStatement(query);
stmt.execute();
this.sendNotification("removeAllEntries", null);
} catch (e) {
this.log("removeEntriesForName failed: " + e);
throw e;
} finally {
stmt.reset();
}
// privacy cleanup, if there's an old mork formhistory around, just delete it
let oldFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
oldFile.append("formhistory.dat");
if (oldFile.exists())
oldFile.remove(false);
},
nameExists : function (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 {
stmt.reset();
}
},
entryExists : function (name, value) {
this.log("entryExists for " + name + "=" + value);
let [id, guid] = this.getExistingEntryID(name, value);
this.log("entryExists: id=" + id);
return (id != -1);
},
removeEntriesByTimeframe : function (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 : beginTime,
endTime : endTime
};
try {
stmt = this.dbCreateStatement(query, params);
stmt.executeStep();
this.sendIntNotification("removeEntriesByTimeframe", beginTime, endTime);
} catch (e) {
this.log("removeEntriesByTimeframe failed: " + e);
throw e;
} finally {
stmt.reset();
}
},
get DBConnection() {
return this.dbConnection;
},
/* ---- nsIObserver interface ---- */
observe : function (subject, topic, data) {
if (topic == "nsPref:changed")
this.updatePrefs();
else
this.log("Oops! Unexpected notification: " + topic);
},
/* ---- nsIFormSubmitObserver interfaces ---- */
notify : function(form, domWin, actionURI, cancelSubmit) {
if (!this.enabled)
return;
this.log("Form submit observer notified.");
if (!this.saveHttpsForms) {
if (actionURI.schemeIs("https"))
return;
if (form.ownerDocument.documentURIObject.schemeIs("https"))
return;
}
if (form.hasAttribute("autocomplete") &&
form.getAttribute("autocomplete").toLowerCase() == "off")
return;
// Open a transaction so that multiple additions are efficient.
this.dbConnection.beginTransaction();
try {
let savedCount = 0;
for (let i = 0; i < form.elements.length; i++) {
let input = form.elements[i];
if (!(input instanceof Ci.nsIDOMHTMLInputElement))
continue;
// Only use inputs that hold text values (not including type="password")
if (!input.mozIsTextField(true))
continue;
// Bug 394612: If Login Manager marked this input, don't save it.
// The login manager will deal with remembering it.
// Don't save values when autocomplete=off is present.
if (input.hasAttribute("autocomplete") &&
input.getAttribute("autocomplete").toLowerCase() == "off")
continue;
let value = input.value.trim();
// Don't save empty or unchanged values.
if (!value || value == input.defaultValue.trim())
continue;
// Don't save credit card numbers.
if (this.isValidCCNumber(value)) {
this.log("skipping saving a credit card number");
continue;
}
let name = input.name || input.id;
if (!name)
continue;
// Limit stored data to 200 characters.
if (name.length > 200 || value.length > 200) {
this.log("skipping input that has a name/value too large");
continue;
}
// Limit number of fields stored per form.
if (savedCount++ >= 100) {
this.log("not saving any more entries for this form.");
break;
}
this.addEntry(name, value);
}
} catch (e) {
// Empty
} finally {
// Save whatever we've added so far.
this.dbConnection.commitTransaction();
}
},
/* ---- helpers ---- */
generateGUID : function() {
// 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 : function (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 : function (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 : function (changeType, data) {
Services.obs.notifyObservers(data, "satchel-storage-changed", changeType);
},
getExistingEntryID : function (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 : 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 {
stmt.reset();
}
return [id, guid];
},
countAllEntries : function () {
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 {
stmt.reset();
}
this.log("countAllEntries: counted entries: " + numEntries);
return numEntries;
},
expireOldEntries : function () {
this.log("expireOldEntries");
// Determine how many days of history we're supposed to keep.
let expireDays = 180;
try {
expireDays = this.prefBranch.getIntPref("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 {
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 = this.prefBranch.getBoolPref("debug");
this.enabled = this.prefBranch.getBoolPref("enable");
this.saveHttpsForms = this.prefBranch.getBoolPref("saveHttpsForms");
},
// Implements the Luhn checksum algorithm as described at
// http://wikipedia.org/wiki/Luhn_algorithm
isValidCCNumber : function (ccNumber) {
// Remove dashes and whitespace
ccNumber = ccNumber.replace(/[\-\s]/g, '');
let len = ccNumber.length;
if (len != 9 && len != 15 && len != 16)
return false;
if (!/^\d+$/.test(ccNumber))
return false;
let total = 0;
for (let i = 0; i < len; i++) {
let ch = parseInt(ccNumber[len - i - 1]);
if (i % 2 == 1) {
// Double it, add digits together if > 10
ch *= 2;
if (ch > 9)
ch -= 9;
}
total += ch;
}
return total % 10 == 0;
},
//**************************************************************************//
// Database Creation & Access
/*
* dbCreateStatement
*
* Creates a statement, wraps it, and then does parameter replacement
* Will use memoization so that statements can be reused.
*/
dbCreateStatement : function (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;
},
/*
* dbInit
*
* Attempts to initialize the database. This creates the file if it doesn't
* exist, performs any migrations, etc.
*/
dbInit : function () {
this.log("Initializing Database");
let storage = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
this.dbConnection = storage.openDatabase(this.dbFile);
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: function () {
this.log("Creating DB -- tables");
for (let name in this.dbSchema.tables) {
let table = this.dbSchema.tables[name];
let tSQL = [[col, table[col]].join(" ") for (col in table)].join(", ");
this.dbConnection.createTable(name, tSQL);
}
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;
},
dbMigrate : function (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 : function () {
// 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 {
stmt.reset();
}
},
/*
* dbMigrateToVersion2
*
* Updates the DB schema to v2 (bug 243136).
* Adds lastUsed index, removes moz_dummy_table
*/
dbMigrateToVersion2 : function () {
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 : function () {
// 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 {
stmt.reset();
}
// Generate a GUID for each login and update the DB.
query = "UPDATE moz_formhistory SET guid = :guid WHERE id = :id";
for each (let id in ids) {
let params = {
id : id,
guid : this.generateGUID()
};
try {
stmt = this.dbCreateStatement(query, params);
stmt.execute();
} catch (e) {
this.log("Failed setting GUID: " + e);
throw e;
} finally {
stmt.reset();
}
}
},
/*
* dbAreExpectedColumnsPresent
*
* Sanity check to ensure that the columns this version of the code expects
* are present in the DB we're using.
*/
dbAreExpectedColumnsPresent : function () {
for (let name in this.dbSchema.tables) {
let table = this.dbSchema.tables[name];
let query = "SELECT " +
[col for (col in 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 : function (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;
}
},
/*
* dbCleanup
*
* Called when database creation fails. Finalizes database statements,
* closes the database connection, deletes the database file.
*/
dbCleanup : function (backup) {
this.log("Cleaning up DB file - close & remove & backup=" + backup)
// Create backup file
if (backup) {
let storage = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
let backupFile = this.dbFile.leafName + ".corrupt";
storage.backupDatabaseFile(this.dbFile, backupFile);
}
// Finalize all statements to free memory, avoid errors later
for each (let stmt in this.dbStmts)
stmt.finalize();
this.dbStmts = [];
// Close the connection, ignore 'already closed' error
try { this.dbConnection.close() } catch(e) {}
this.dbFile.remove(false);
}
};
let component = [FormHistory];
function NSGetModule (compMgr, fileSpec) {
return XPCOMUtils.generateModule(component);
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,149 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Joe Hewitt <hewitt@netscape.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __nsFormHistory__
#define __nsFormHistory__
#include "nsIFormHistory.h"
#include "nsIFormSubmitObserver.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsIPrefBranch.h"
#include "nsIUUIDGenerator.h"
#include "nsWeakReference.h"
#include "nsIMutableArray.h"
#include "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "nsServiceManagerUtils.h"
#include "nsToolkitCompsCID.h"
class nsIAutoCompleteSimpleResult;
class nsIAutoCompleteResult;
class nsFormHistory;
class nsIObserverService;
template <class E> class nsTArray;
#define NS_IFORMHISTORYPRIVATE_IID \
{0xc4a47315, 0xaeb5, 0x4039, {0x9f, 0x34, 0x45, 0x11, 0xb3, 0xa7, 0x58, 0xdd}}
class nsIFormHistoryPrivate : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IFORMHISTORYPRIVATE_IID)
mozIStorageConnection* GetStorageConnection() { return mDBConn; }
protected:
nsCOMPtr<mozIStorageConnection> mDBConn;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFormHistoryPrivate, NS_IFORMHISTORYPRIVATE_IID)
class nsFormHistory : public nsIFormHistory2,
public nsIFormHistoryPrivate,
public nsIObserver,
public nsIFormSubmitObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFORMHISTORY2
NS_DECL_NSIOBSERVER
// nsIFormSubmitObserver
NS_IMETHOD Notify(nsIDOMHTMLFormElement* formElt, nsIDOMWindowInternal* window, nsIURI* actionURL, PRBool* cancelSubmit);
nsFormHistory();
nsresult Init();
private:
~nsFormHistory();
bool IsValidCCNumber(const nsAString &aString);
protected:
// Database I/O
nsresult OpenDatabase(PRBool *aDoImport);
nsresult CloseDatabase();
nsresult GetDatabaseFile(nsIFile** aFile);
nsresult dbMigrate();
nsresult dbCleanup();
nsresult MigrateToVersion1();
nsresult MigrateToVersion2();
nsresult MigrateToVersion3();
PRBool dbAreExpectedColumnsPresent();
nsresult CreateTable();
nsresult CreateStatements();
static nsresult InitPrefs();
static PRBool SaveHttpsForms();
static PRBool FormHistoryEnabled();
static nsFormHistory *gFormHistory;
static PRBool gFormHistoryEnabled;
static PRBool gSaveHttpsForms;
static PRBool gPrefsInitialized;
nsresult GenerateGUID(nsACString &guid);
nsresult ExpireOldEntries();
PRInt32 CountAllEntries();
PRInt64 GetExistingEntryID (const nsAString &aName, const nsAString &aValue);
PRInt64 GetExistingEntryID (const nsAString &aName, const nsAString &aValue, nsAString &aGuid);
nsresult SendNotification(const nsAString &aChangeType, nsISupports *aData);
nsresult SendNotification(const nsAString &aChangeType, const nsAString &aName);
nsresult SendNotification(const nsAString &aChangeType, const nsAString &aName, const nsAString &aValue, const nsAutoString &aGuid);
nsresult SendNotification(const nsAString &aChangeType, const PRInt64 &aNumber);
nsresult SendNotification(const nsAString &aChangeType, const PRInt64 &aOne, const PRInt64 &aTwo);
nsCOMPtr<nsIUUIDGenerator> mUUIDService;
nsCOMPtr<nsIPrefBranch> mPrefBranch;
nsCOMPtr<nsIObserverService> mObserverService;
nsCOMPtr<mozIStorageService> mStorageService;
nsCOMPtr<mozIStorageStatement> mDBFindEntry;
nsCOMPtr<mozIStorageStatement> mDBFindEntryByName;
nsCOMPtr<mozIStorageStatement> mDBSelectEntries;
nsCOMPtr<mozIStorageStatement> mDBInsertNameValue;
nsCOMPtr<mozIStorageStatement> mDBUpdateEntry;
};
#endif // __nsFormHistory__