Bug 463471 - temp tables are not correctly synced to disk when the user clear private data on shutdown (r=sdwilsh, r=dietrich)

This commit is contained in:
mak77@bonardo.net 2008-11-13 22:45:49 -08:00
Родитель 7adcee4fcc
Коммит 396bdf1f01
15 изменённых файлов: 403 добавлений и 33 удалений

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

@ -46,11 +46,24 @@ interface mozIStorageConnection;
* database. If outside consumers wish to use this, they should only read from
* the database so they do not break any internal invariants.
*/
[scriptable, uuid(daa7d3ba-8e24-4228-93b5-2c188bad7d36)]
[scriptable, uuid(8e6d4f8a-4b8e-4026-9fca-517c4494ddb7)]
interface nsPIPlacesDatabase : nsISupports
{
/**
* The database connection used by Places.
*/
readonly attribute mozIStorageConnection DBConnection;
/**
* Finalizes all Places internal statements, allowing to safely close the
* database connection.
*/
void finalizeInternalStatements();
/**
* Commits all pending history changes, call this before finalizing
* statements and closing the database connection to ensure safety for all
* history data.
*/
void commitPendingChanges();
};

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

@ -2007,3 +2007,31 @@ nsAnnotationService::CallSetForItemObservers(PRInt64 aItemId, const nsACString&
for (PRInt32 i = 0; i < mObservers.Count(); i ++)
mObservers[i]->OnItemAnnotationSet(aItemId, aName);
}
nsresult
nsAnnotationService::FinalizeStatements() {
mozIStorageStatement* stmts[] = {
mDBSetAnnotation,
mDBSetItemAnnotation,
mDBGetAnnotation,
mDBGetItemAnnotation,
mDBGetAnnotationNames,
mDBGetItemAnnotationNames,
mDBGetAnnotationFromURI,
mDBGetAnnotationFromItemId,
mDBGetAnnotationNameID,
mDBAddAnnotationName,
mDBAddAnnotation,
mDBAddItemAnnotation,
mDBRemoveAnnotation,
mDBRemoveItemAnnotation,
mDBGetItemsWithAnnotation
};
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {
nsresult rv = nsNavHistory::FinalizeStatement(stmts[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}

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

@ -78,6 +78,11 @@ public:
return gAnnotationService;
}
/**
* Finalize all internal statements.
*/
nsresult FinalizeStatements();
NS_DECL_ISUPPORTS
NS_DECL_NSIANNOTATIONSERVICE

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

@ -1011,6 +1011,24 @@ nsFaviconService::OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
return NS_OK;
}
nsresult
nsFaviconService::FinalizeStatements() {
mozIStorageStatement* stmts[] = {
mDBGetURL,
mDBGetData,
mDBGetIconInfo,
mDBInsertIcon,
mDBUpdateIcon,
mDBSetPageFavicon
};
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {
nsresult rv = nsNavHistory::FinalizeStatement(stmts[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_IMPL_ISUPPORTS4(FaviconLoadListener,
nsIRequestObserver,

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

@ -90,10 +90,15 @@ public:
static nsresult OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen,
const nsACString& aMimeType,
nsACString& aNewData, nsACString& aNewMimeType);
/**
* Finalize all internal statements.
*/
nsresult FinalizeStatements();
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONSERVICE
private:
~nsFaviconService();

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

@ -432,6 +432,35 @@ nsNavBookmarks::InitStatements()
return NS_OK;
}
nsresult
nsNavBookmarks::FinalizeStatements() {
mozIStorageStatement* stmts[] = {
mDBGetChildren,
mDBFindURIBookmarks,
mDBFolderCount,
mDBGetItemIndex,
mDBGetChildAt,
mDBGetItemProperties,
mDBGetItemIdForGUID,
mDBGetRedirectDestinations,
mDBInsertBookmark,
mDBIsBookmarkedInDatabase,
mDBGetLastBookmarkID,
mDBSetItemDateAdded,
mDBSetItemLastModified,
mDBSetItemIndex,
mDBGetKeywordForURI,
mDBGetKeywordForBookmark,
mDBGetURIForKeyword
};
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {
nsresult rv = nsNavHistory::FinalizeStatement(stmts[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// nsNavBookmarks::InitRoots
//

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

@ -104,6 +104,11 @@ public:
PRBool ItemExists(PRInt64 aItemId);
/**
* Finalize all internal statements.
*/
nsresult FinalizeStatements();
private:
static nsNavBookmarks *sInstance;

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

@ -5279,6 +5279,57 @@ nsNavHistory::GetDBConnection(mozIStorageConnection **_DBConnection)
return NS_OK;
}
NS_IMETHODIMP
nsNavHistory::FinalizeInternalStatements()
{
NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
#ifdef LAZY_ADD
// Kill lazy timer or it could fire later when statements won't be valid
// anymore.
// At this point we should have called CommitPendingChanges before the last
// sync, so all data is saved to disk and we can finalize all statements.
if (mLazyTimer)
mLazyTimer->Cancel();
NS_ABORT_IF_FALSE(mLazyMessages.Length() == 0,
"There are pending lazy messages, did you call CommitPendingChanges()?");
#endif
// nsNavHistory
nsresult rv = FinalizeStatements();
NS_ENSURE_SUCCESS(rv, rv);
// nsNavBookmarks
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
rv = bookmarks->FinalizeStatements();
NS_ENSURE_SUCCESS(rv, rv);
// nsAnnotationService
nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
rv = annosvc->FinalizeStatements();
NS_ENSURE_SUCCESS(rv, rv);
// nsFaviconService
nsFaviconService* iconsvc = nsFaviconService::GetFaviconService();
NS_ENSURE_TRUE(iconsvc, NS_ERROR_OUT_OF_MEMORY);
rv = iconsvc->FinalizeStatements();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsNavHistory::CommitPendingChanges()
{
#ifdef LAZY_ADD
CommitLazyMessages();
#endif
return NS_OK;
}
// nsIObserver *****************************************************************
NS_IMETHODIMP
@ -7455,6 +7506,50 @@ nsNavHistory::GetDBOldFrecencies()
return mDBOldFrecencies;
}
nsresult
nsNavHistory::FinalizeStatements() {
mozIStorageStatement* stmts[] = {
mDBGetURLPageInfo,
mDBGetIdPageInfo,
mDBRecentVisitOfURL,
mDBRecentVisitOfPlace,
mDBInsertVisit,
mDBGetPageVisitStats,
mDBIsPageVisited,
mDBUpdatePageVisitStats,
mDBAddNewPage,
mDBGetTags,
mFoldersWithAnnotationQuery,
mDBSetPlaceTitle,
mDBVisitToURLResult,
mDBVisitToVisitResult,
mDBBookmarkToUrlResult,
mDBVisitsForFrecency,
mDBUpdateFrecencyAndHidden,
mDBGetPlaceVisitStats,
mDBGetBookmarkParentsForPlace,
mDBFullVisitCount,
mDBInvalidFrecencies,
mDBOldFrecencies,
mDBCurrentQuery,
mDBAutoCompleteQuery,
mDBAutoCompleteHistoryQuery,
mDBAutoCompleteStarQuery,
mDBAutoCompleteTagsQuery,
mDBPreviousQuery,
mDBAdaptiveQuery,
mDBKeywordQuery,
mDBFeedbackIncrease
};
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) {
nsresult rv = nsNavHistory::FinalizeStatement(stmts[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// nsICharsetResolver **********************************************************
NS_IMETHODIMP

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

@ -197,17 +197,6 @@ public:
return gHistoryService;
}
/**
* Call this function before doing any database reads. It will ensure that
* any data not flushed to the DB yet is flushed.
*/
void SyncDB()
{
#ifdef LAZY_ADD
CommitLazyMessages();
#endif
}
#ifdef LAZY_ADD
/**
* Adds a lazy message for adding a favicon. Used by the favicon service so
@ -400,6 +389,19 @@ public:
typedef nsDataHashtable<nsCStringHashKey, nsCString> StringHash;
/**
* Helper method to finalize a statement
*/
static nsresult
FinalizeStatement(mozIStorageStatement *aStatement) {
nsresult rv;
if (aStatement) {
rv = aStatement->Finalize();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
private:
~nsNavHistory();
@ -444,6 +446,11 @@ protected:
mozIStorageStatement *GetDBBookmarkToUrlResult();
nsCOMPtr<mozIStorageStatement> mDBBookmarkToUrlResult; // kGetInfoIndex_* results
/**
* Finalize all internal statements.
*/
nsresult FinalizeStatements();
// nsICharsetResolver
NS_DECL_NSICHARSETRESOLVER

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

@ -257,18 +257,10 @@ nsNavHistoryExpire::ClearHistory()
"SELECT h.id FROM moz_places_temp h "
"WHERE "
"EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) "
"OR EXISTS "
"(SELECT id FROM moz_annos WHERE place_id = h.id AND expiration = ") +
nsPrintfCString("%d", nsIAnnotationService::EXPIRE_NEVER) +
NS_LITERAL_CSTRING(") "
"UNION ALL "
"SELECT h.id FROM moz_places h "
"WHERE "
"EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) "
"OR EXISTS "
"(SELECT id FROM moz_annos WHERE place_id = h.id AND expiration = ") +
nsPrintfCString("%d", nsIAnnotationService::EXPIRE_NEVER) +
NS_LITERAL_CSTRING(") "
")"));
NS_ENSURE_SUCCESS(rv, rv);
@ -837,7 +829,9 @@ nsNavHistoryExpire::ExpireAnnotations(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = expirePagesStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = expirePagesStatement->Reset();
NS_ENSURE_SUCCESS(rv, rv);
// remove days item annos
rv = expireItemsStatement->BindInt32Parameter(0, nsIAnnotationService::EXPIRE_DAYS);
NS_ENSURE_SUCCESS(rv, rv);
@ -845,6 +839,8 @@ nsNavHistoryExpire::ExpireAnnotations(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = expireItemsStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = expireItemsStatement->Reset();
NS_ENSURE_SUCCESS(rv, rv);
// remove weeks annos
rv = expirePagesStatement->BindInt32Parameter(0, nsIAnnotationService::EXPIRE_WEEKS);
@ -853,6 +849,8 @@ nsNavHistoryExpire::ExpireAnnotations(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = expirePagesStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = expirePagesStatement->Reset();
NS_ENSURE_SUCCESS(rv, rv);
// remove weeks item annos
rv = expireItemsStatement->BindInt32Parameter(0, nsIAnnotationService::EXPIRE_WEEKS);
@ -861,6 +859,8 @@ nsNavHistoryExpire::ExpireAnnotations(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = expireItemsStatement->Execute();
NS_ENSURE_SUCCESS(rv, rv);
rv = expireItemsStatement->Reset();
NS_ENSURE_SUCCESS(rv, rv);
// remove months annos
rv = expirePagesStatement->BindInt32Parameter(0, nsIAnnotationService::EXPIRE_MONTHS);

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

@ -2195,7 +2195,10 @@ nsNavHistoryQueryResultNode::GetHasChildren(PRBool* aHasChildren)
rv = hasTagsStatement->BindInt64Parameter(0, tagsFolderId);
NS_ENSURE_SUCCESS(rv, rv);
return hasTagsStatement->ExecuteStep(aHasChildren);
rv = hasTagsStatement->ExecuteStep(aHasChildren);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// For history containers query we must check if we have any history

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

@ -103,7 +103,26 @@ nsPlacesDBFlush.prototype = {
this._prefs.QueryInterface(Ci.nsIPrefBranch2).removeObserver("", this);
this._timer.cancel();
this._timer = null;
this._syncTables(["places", "historyvisits"]);
// Other components could still make changes to history at this point,
// for example to clear private data on shutdown, so here we dispatch
// an event to the main thread so that we will sync after
// quit-application ensuring all data have been saved.
let tm = Cc["@mozilla.org/thread-manager;1"].
getService(Ci.nsIThreadManager);
tm.mainThread.dispatch({
_self: this,
run: function() {
let pip = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase);
pip.commitPendingChanges();
this._self._syncTables(["places", "historyvisits"]);
// Close the database connection, this was the last sync and we can't
// ensure database coherence from now on.
pip.finalizeInternalStatements();
this._self._db.close();
}
}, Ci.nsIThread.DISPATCH_NORMAL);
}
else if (aTopic == "nsPref:changed" && aData == kSyncPrefName) {
// Get the new pref value, and then update our timer

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

@ -111,9 +111,7 @@ clearDB();
*/
function dump_table(aName)
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let db = DBConn()
let stmt = db.createStatement("SELECT * FROM " + aName);
dump("\n*** Printing data from " + aName + ":\n");
@ -185,9 +183,7 @@ function finish_test()
*/
function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected, aFinish)
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let db = DBConn();
let stmt = db.createStatement(
"SELECT moz_places.url " +
"FROM moz_bookmarks INNER JOIN moz_places " +
@ -227,9 +223,7 @@ function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected, aFini
*/
function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish)
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
let db = DBConn();
let stmt = db.createStatement(
"SELECT moz_places.url " +
"FROM moz_historyvisits INNER JOIN moz_places " +
@ -253,6 +247,31 @@ function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish)
finish_test();
}
/**
* Function gets current database connection, if the connection has been closed
* it will try to reconnect to the places.sqlite database.
*/
function DBConn()
{
let db = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsPIPlacesDatabase).
DBConnection;
if (db.connectionReady)
return db;
// open a new connection if needed
let file = dirSvc.get('ProfD', Ci.nsIFile);
file.append("places.sqlite");
let storageService = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
try {
var dbConn = storageService.openDatabase(file);
} catch (ex) {
return null;
}
return dbConn;
}
// profile-after-change doesn't create components in xpcshell, so we have to do
// it ourselves
Cc["@mozilla.org/places/sync;1"].getService(Ci.nsISupports);

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

@ -0,0 +1,75 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** 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 Places Unit Test Code.
*
* The Initial Developer of the Original Code is Mozilla Corp.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77bonardo.net> (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 hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var pip = hs.QueryInterface(Ci.nsPIPlacesDatabase);
var mDBConn = pip.DBConnection;
var gh = hs.QueryInterface(Ci.nsIGlobalHistory2);
var iconsvc = Cc["@mozilla.org/browser/favicon-service;1"].
getService(Ci.nsIFaviconService);
const TEST_URI = "http://www.mozilla.org/";
const TEST_TITLE = "testTitle";
// main
function run_test() {
var testURI = uri(TEST_URI);
var faviconURI = uri(TEST_URI + "favicon.ico");
// Add a uri lazy message
gh.addURI(testURI, false, true, null);
// Add a favicon lazy message
iconsvc.setFaviconUrlForPage(testURI, faviconURI);
// Add a title lazy message
hs.setPageTitle(testURI, TEST_TITLE);
// Commit all pending changes (lazy messages)
pip.commitPendingChanges();
// Check database values, we can't use APIs because them would check
// lazy queue.
let stmt = mDBConn.createStatement(
"SELECT id FROM moz_places_temp WHERE url = :url AND title = :title " +
"AND favicon_id = (SELECT id FROM moz_favicons WHERE url = :favicon_url)");
stmt.params["url"] = testURI.spec;
stmt.params["title"] = TEST_TITLE;
stmt.params["favicon_url"] = faviconURI.spec;
do_check_true(stmt.executeStep());
stmt.finalize();
}

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

@ -0,0 +1,49 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** 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 Places Unit Test Code.
*
* The Initial Developer of the Original Code is Mozilla Corp.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77bonardo.net> (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 ***** */
// main
function run_test() {
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var mDBConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
hs.QueryInterface(Ci.nsPIPlacesDatabase).finalizeInternalStatements();
mDBConn.close();
do_check_false(mDBConn.connectionReady);
}