Bug 506402 - Add GUIDs to satchel form entries. r=zpao, r=gavin

This commit is contained in:
Justin Dolske 2010-04-13 15:18:31 -07:00
Родитель 628a008cf4
Коммит 5fdca1b890
8 изменённых файлов: 295 добавлений и 5 удалений

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

@ -43,6 +43,8 @@
#include "nsStorageFormHistory.h"
#include "plbase64.h"
#include "prmem.h"
#include "nsIServiceManager.h"
#include "nsIObserverService.h"
#include "nsICategoryManager.h"
@ -69,7 +71,7 @@
#include "nsIPrivateBrowsingService.h"
#include "nsNetCID.h"
#define DB_SCHEMA_VERSION 2
#define DB_SCHEMA_VERSION 3
#define DB_FILENAME NS_LITERAL_STRING("formhistory.sqlite")
#define DB_CORRUPT_FILENAME NS_LITERAL_STRING("formhistory.sqlite.corrupt")
@ -124,6 +126,9 @@ nsFormHistory::Init()
{
PRBool doImport;
mUUIDService = do_GetService("@mozilla.org/uuid-generator;1");
NS_ENSURE_TRUE(mUUIDService, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = OpenDatabase(&doImport);
if (rv == NS_ERROR_FILE_CORRUPTED) {
/* If the DB is corrupt, nuke it and try again with a new DB. */
@ -182,6 +187,22 @@ nsFormHistory::FormHistoryEnabled()
return gFormHistoryEnabled;
}
nsresult
nsFormHistory::GenerateGUID(nsACString &guidString) {
nsID rawguid;
nsresult rv = mUUIDService->GenerateUUIDInPlace(&rawguid);
NS_ENSURE_SUCCESS(rv, rv);
// Encode 12 bytes (96bits) of randomness into a 16 character base-64 string.
char *b64 = PL_Base64Encode(reinterpret_cast<const char *>(&rawguid), 12, nsnull);
if (!b64)
return NS_ERROR_OUT_OF_MEMORY;
guidString.Assign(b64);
PR_Free(b64);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////
//// nsIFormHistory2
@ -229,6 +250,10 @@ nsFormHistory::AddEntry(const nsAString &aName, const nsAString &aValue)
rv = mDBUpdateEntry->Execute();
NS_ENSURE_SUCCESS(rv, rv);
} else {
nsCAutoString guid;
rv = GenerateGUID(guid);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 now = PR_Now();
mozStorageStatementScoper scope(mDBInsertNameValue);
@ -247,6 +272,9 @@ nsFormHistory::AddEntry(const nsAString &aName, const nsAString &aValue)
// lastUsed
rv = mDBInsertNameValue->BindInt64Parameter(4, now);
NS_ENSURE_SUCCESS(rv, rv);
// guid
rv = mDBInsertNameValue->BindUTF8StringParameter(5, guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBInsertNameValue->Execute();
NS_ENSURE_SUCCESS(rv, rv);
@ -601,12 +629,14 @@ nsFormHistory::CreateTable()
"CREATE TABLE moz_formhistory ("
"id INTEGER PRIMARY KEY, fieldname TEXT NOT NULL, "
"value TEXT NOT NULL, timesUsed INTEGER, "
"firstUsed INTEGER, lastUsed INTEGER)"));
"firstUsed INTEGER, lastUsed INTEGER, guid TEXT)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_formhistory_index ON moz_formhistory (fieldname)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_formhistory_lastused_index ON moz_formhistory (lastUsed)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX moz_formhistory_guid_index ON moz_formhistory (guid)"));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBConn->SetSchemaVersion(DB_SCHEMA_VERSION);
NS_ENSURE_SUCCESS(rv, rv);
@ -635,7 +665,7 @@ nsFormHistory::CreateStatements()
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"INSERT INTO moz_formhistory (fieldname, value, timesUsed, "
"firstUsed, lastUsed) VALUES (?1, ?2, ?3, ?4, ?5)"),
"firstUsed, lastUsed, guid) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"),
getter_AddRefs(mDBInsertNameValue));
NS_ENSURE_SUCCESS(rv, rv);
@ -712,6 +742,10 @@ nsFormHistory::dbMigrate()
rv = MigrateToVersion2();
NS_ENSURE_SUCCESS(rv, rv);
// (fallthrough to the next upgrade)
case 2:
rv = MigrateToVersion3();
NS_ENSURE_SUCCESS(rv, rv);
// (fallthrough to the next upgrade)
case DB_SCHEMA_VERSION:
// (current version, nothing more to do)
break;
@ -814,6 +848,72 @@ nsFormHistory::MigrateToVersion2()
}
/*
* MigrateToVersion3
*
* Updates the DB schema to v3 (bug 506402).
* Adds guid column and index.
*/
nsresult
nsFormHistory::MigrateToVersion3()
{
nsresult rv;
// Check to see if the new column already exists (could be a v3 DB that
// was downgraded to v2). If they exist, we don't need to add them.
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT guid FROM moz_formhistory"),
getter_AddRefs(stmt));
PRBool columnExists = !!NS_SUCCEEDED(rv);
if (!columnExists) {
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_formhistory ADD COLUMN guid TEXT"));
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS moz_formhistory_guid_index ON moz_formhistory (guid)"));
NS_ENSURE_SUCCESS(rv, rv);
mozStorageTransaction transaction(mDBConn, PR_FALSE);
nsCOMPtr<mozIStorageStatement> selectStatement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id FROM moz_formhistory WHERE guid isnull"),
getter_AddRefs(selectStatement));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> updateStatement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_formhistory SET guid = ?1 WHERE id = ?2"),
getter_AddRefs(updateStatement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasMore;
while (NS_SUCCEEDED(selectStatement->ExecuteStep(&hasMore)) && hasMore) {
PRUint64 id = selectStatement->AsInt64(0);
nsCAutoString guid;
rv = GenerateGUID(guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStatement->BindInt64Parameter(1, id);
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStatement->BindUTF8StringParameter(0, guid);
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
rv = mDBConn->SetSchemaVersion(3);
NS_ENSURE_SUCCESS(rv, rv);
return transaction.Commit();
}
nsresult
nsFormHistory::GetDatabaseFile(nsIFile** aFile)
{
@ -860,7 +960,7 @@ nsFormHistory::dbAreExpectedColumnsPresent()
// If the statement succeeds, all the columns are there.
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT fieldname, value, timesUsed, firstUsed, lastUsed "
"SELECT fieldname, value, timesUsed, firstUsed, lastUsed, guid "
"FROM moz_formhistory"), getter_AddRefs(stmt));
return NS_SUCCEEDED(rv) ? PR_TRUE : PR_FALSE;
}

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

@ -45,6 +45,7 @@
#include "nsCOMPtr.h"
#include "nsIObserver.h"
#include "nsIPrefBranch.h"
#include "nsIUUIDGenerator.h"
#include "nsWeakReference.h"
#include "mozIStorageService.h"
@ -106,6 +107,7 @@ public:
nsresult dbCleanup();
nsresult MigrateToVersion1();
nsresult MigrateToVersion2();
nsresult MigrateToVersion3();
PRBool dbAreExpectedColumnsPresent();
nsresult CreateTable();
@ -119,10 +121,12 @@ public:
static PRBool gSaveHttpsForms;
static PRBool gPrefsInitialized;
nsresult GenerateGUID(nsACString &guid);
nsresult ExpireOldEntries();
PRInt32 CountAllEntries();
PRInt64 GetExistingEntryID(const nsAString &aName, const nsAString &aValue);
nsCOMPtr<nsIUUIDGenerator> mUUIDService;
nsCOMPtr<nsIPrefBranch> mPrefBranch;
nsCOMPtr<mozIStorageService> mStorageService;
nsCOMPtr<mozIStorageStatement> mDBFindEntry;

Двоичные данные
toolkit/components/satchel/test/unit/formhistory_v2.sqlite Normal file

Двоичный файл не отображается.

Двоичные данные
toolkit/components/satchel/test/unit/formhistory_v2v3.sqlite Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

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

@ -38,7 +38,7 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
const CURRENT_SCHEMA = 2;
const CURRENT_SCHEMA = 3;
const PR_HOURS = 60 * 60 * 1000000;
do_get_profile();
@ -55,3 +55,13 @@ function getDBVersion(dbfile) {
return version;
}
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;
}

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

@ -0,0 +1,85 @@
/* ***** 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 Satchel Test Code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.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 ***** */
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;
}
}

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

@ -0,0 +1,91 @@
/* ***** 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 Satchel Test Code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Justin Dolske <dolske@mozilla.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 ***** */
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;
}
}