зеркало из https://github.com/mozilla/gecko-dev.git
initial fix for 55293 - expire pages in history on shutdown
mostly stuff submitted by bienvenu, r=alecf, the rest by me, r=bienvenu sr=waterson
This commit is contained in:
Родитель
508c2cdc93
Коммит
a7b08e468a
|
@ -77,8 +77,8 @@ nsIRDFResource* nsGlobalHistory::kNC_HistoryByDate;
|
|||
#define DEBUG_LAST_PAGE_VISITED 1
|
||||
#endif /* DEBUG_sspitzer */
|
||||
|
||||
#define BROWSER_HISTORY_LAST_PAGE_VISITED_PREF "browser.history.last_page_visited"
|
||||
|
||||
#define PREF_BROWSER_HISTORY_LAST_PAGE_VISITED "browser.history.last_page_visited"
|
||||
#define PREF_BROWSER_HISTORY_EXPIRE_DAYS "browser.history_expire_days"
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// CIDs
|
||||
|
@ -256,9 +256,11 @@ nsMdbTableEnumerator::GetNext(nsISupports** _result)
|
|||
nsGlobalHistory::nsGlobalHistory()
|
||||
: mEnv(nsnull),
|
||||
mStore(nsnull),
|
||||
mTable(nsnull)
|
||||
mTable(nsnull),
|
||||
mExpireDays(9) // make default be nine days
|
||||
{
|
||||
NS_INIT_REFCNT();
|
||||
LL_I2L(mFileSizeOnDisk, 0);
|
||||
}
|
||||
|
||||
nsGlobalHistory::~nsGlobalHistory()
|
||||
|
@ -296,7 +298,12 @@ nsGlobalHistory::~nsGlobalHistory()
|
|||
//
|
||||
// nsISupports methods
|
||||
|
||||
NS_IMPL_ISUPPORTS3(nsGlobalHistory, nsIGlobalHistory, nsIRDFDataSource, nsIRDFRemoteDataSource)
|
||||
NS_IMPL_ISUPPORTS5(nsGlobalHistory,
|
||||
nsIGlobalHistory,
|
||||
nsIObserver,
|
||||
nsISupportsWeakReference,
|
||||
nsIRDFDataSource,
|
||||
nsIRDFRemoteDataSource)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
|
@ -398,6 +405,10 @@ nsGlobalHistory::AddPageToDatabase(const char *aURL,
|
|||
rv = NotifyChange(url, kNC_Date, oldDateLiteral, date);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// ...now set the new date.
|
||||
char buf[64];
|
||||
PRInt64ToChars(aDate, buf, sizeof(buf));
|
||||
|
||||
// visit count
|
||||
nsCOMPtr<nsIRDFInt> oldCountLiteral;
|
||||
rv = gRDFService->GetIntLiteral(oldCount, getter_AddRefs(oldCountLiteral));
|
||||
|
@ -696,24 +707,7 @@ nsGlobalHistory::RemoveAllPages()
|
|||
NS_ASSERTION(err == 0, "error ending batch");
|
||||
|
||||
// Do a "large commit" to flush the (almost empty) table to disk.
|
||||
{
|
||||
nsMdbPtr<nsIMdbThumb> thumb(mEnv);
|
||||
err = mStore->CompressCommit(mEnv, getter_Acquires(thumb));
|
||||
if (err != 0) return NS_ERROR_FAILURE;
|
||||
|
||||
mdb_count total;
|
||||
mdb_count current;
|
||||
mdb_bool done;
|
||||
mdb_bool broken;
|
||||
|
||||
do {
|
||||
err = thumb->DoMore(mEnv, &total, ¤t, &done, &broken);
|
||||
} while ((err == 0) && !broken && !done);
|
||||
|
||||
if ((err != 0) || !done) return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return Commit(kCompressCommit);
|
||||
}
|
||||
|
||||
|
||||
|
@ -769,7 +763,7 @@ nsGlobalHistory::SaveLastPageVisited(const char *aURL)
|
|||
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = prefs->SetCharPref(BROWSER_HISTORY_LAST_PAGE_VISITED_PREF, aURL);
|
||||
rv = prefs->SetCharPref(PREF_BROWSER_HISTORY_LAST_PAGE_VISITED, aURL);
|
||||
|
||||
#ifdef DEBUG_LAST_PAGE_VISITED
|
||||
printf("XXX saving last page visited as: %s\n", aURL);
|
||||
|
@ -789,7 +783,7 @@ nsGlobalHistory::GetLastPageVisited(char **_retval)
|
|||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsXPIDLCString lastPageVisited;
|
||||
rv = prefs->CopyCharPref(BROWSER_HISTORY_LAST_PAGE_VISITED_PREF, getter_Copies(lastPageVisited));
|
||||
rv = prefs->CopyCharPref(PREF_BROWSER_HISTORY_LAST_PAGE_VISITED, getter_Copies(lastPageVisited));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
*_retval = nsCRT::strdup((const char *)lastPageVisited);
|
||||
|
@ -1509,25 +1503,7 @@ nsGlobalHistory::Refresh(PRBool aBlocking)
|
|||
NS_IMETHODIMP
|
||||
nsGlobalHistory::Flush()
|
||||
{
|
||||
nsMdbPtr<nsIMdbThumb> thumb(mEnv);
|
||||
mdb_err err;
|
||||
|
||||
err = mStore->LargeCommit(mEnv, getter_Acquires(thumb));
|
||||
if (err != 0) return NS_ERROR_FAILURE;
|
||||
|
||||
mdb_count total;
|
||||
mdb_count current;
|
||||
mdb_bool done;
|
||||
mdb_bool broken;
|
||||
|
||||
do
|
||||
{
|
||||
err = thumb->DoMore(mEnv, &total, ¤t, &done, &broken);
|
||||
} while ((err == 0) && !broken && !done);
|
||||
|
||||
if ((err != 0) || !done) return NS_ERROR_FAILURE;
|
||||
|
||||
return NS_OK;
|
||||
return Commit(kLargeCommit);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1544,6 +1520,13 @@ nsGlobalHistory::Init()
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
// we'd like to get this pref when we need it, but at that point,
|
||||
// we can't get the pref service. This means if the user changes
|
||||
// this pref, we won't notice until the next time we run.
|
||||
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = prefs->GetIntPref(PREF_BROWSER_HISTORY_EXPIRE_DAYS, &mExpireDays);
|
||||
|
||||
if (gRefCnt++ == 0) {
|
||||
rv = nsServiceManager::GetService(kRDFServiceCID,
|
||||
NS_GET_IID(nsIRDFService),
|
||||
|
@ -1766,32 +1749,191 @@ nsGlobalHistory::CreateTokens()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsGlobalHistory::Commit(eCommitType commitType)
|
||||
{
|
||||
nsresult err = NS_OK;
|
||||
nsMdbPtr<nsIMdbThumb> thumb(mEnv);
|
||||
|
||||
if (!mStore)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
if (commitType == kLargeCommit || commitType == kSessionCommit)
|
||||
{
|
||||
mdb_percent outActualWaste = 0;
|
||||
mdb_bool outShould;
|
||||
if (mStore)
|
||||
{
|
||||
// check how much space would be saved by doing a compress commit.
|
||||
// If it's more than 30%, go for it.
|
||||
// N.B. - I'm not sure this calls works in Mork for all cases.
|
||||
err = mStore->ShouldCompress(mEnv, 30, &outActualWaste, &outShould);
|
||||
if (NS_SUCCEEDED(err) && outShould)
|
||||
{
|
||||
commitType = kCompressCommit;
|
||||
}
|
||||
else
|
||||
{
|
||||
mdb_count count;
|
||||
err = mTable->GetCount(mEnv, &count);
|
||||
// Since Mork's shouldCompress doesn't work, we need to look at the file
|
||||
// size and the number of rows, and make a stab at guessing if we've got
|
||||
// a lot of deleted rows. The file size is the size when we opened the db,
|
||||
// and we really want it to be the size after we've written out the file,
|
||||
// but I think this is a good enough approximation.
|
||||
PRInt64 numRows;
|
||||
PRInt64 bytesPerRow;
|
||||
PRInt64 desiredAvgRowSize;
|
||||
|
||||
LL_UI2L(numRows, count);
|
||||
LL_DIV(bytesPerRow, mFileSizeOnDisk, numRows);
|
||||
LL_I2L(desiredAvgRowSize, 400);
|
||||
if (LL_CMP(bytesPerRow, >, desiredAvgRowSize))
|
||||
commitType = kCompressCommit;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (commitType)
|
||||
{
|
||||
case kLargeCommit:
|
||||
err = mStore->LargeCommit(mEnv, getter_Acquires(thumb));
|
||||
break;
|
||||
case kSessionCommit:
|
||||
err = mStore->SessionCommit(mEnv, getter_Acquires(thumb));
|
||||
break;
|
||||
case kCompressCommit:
|
||||
err = mStore->CompressCommit(mEnv, getter_Acquires(thumb));
|
||||
break;
|
||||
}
|
||||
if (err == 0) {
|
||||
mdb_count total;
|
||||
mdb_count current;
|
||||
mdb_bool done;
|
||||
mdb_bool broken;
|
||||
|
||||
do {
|
||||
err = thumb->DoMore(mEnv, &total, ¤t, &done, &broken);
|
||||
} while ((err == 0) && !broken && !done);
|
||||
}
|
||||
if (err != 0) // mork doesn't return NS error codes. Yet.
|
||||
return NS_ERROR_FAILURE;
|
||||
else
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
// if notify is true, we'll notify rdf of deleted rows.
|
||||
// If we're shutting down history, then (maybe?) we don't
|
||||
// need or want to notify rdf.
|
||||
nsresult nsGlobalHistory::ExpireEntries(PRBool notify)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// we'd like to get the expire_days pref here, "browser.history_expire_days",
|
||||
// but by the time we're called, we can't get the pref service because we're
|
||||
// shutting down!
|
||||
|
||||
// Iterate through all the rows in the table, notifying observers
|
||||
// that they're going away and cutting each out of the database.
|
||||
// Start with a cursor across the entire table.
|
||||
mdb_err err;
|
||||
mdb_count count;
|
||||
err = mTable->GetCount(mEnv, &count);
|
||||
if (err != 0) return NS_ERROR_FAILURE;
|
||||
|
||||
// Begin the batch.
|
||||
int marker;
|
||||
err = mTable->StartBatchChangeHint(mEnv, &marker);
|
||||
NS_ASSERTION(err == 0, "unable to start batch");
|
||||
if (err != 0) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIRDFResource> resource;
|
||||
|
||||
PRTime expirationDate;
|
||||
PRTime now = PR_Now();
|
||||
PRInt64 microSecondsPerSecond, secondsInDays, microSecondsInExpireDays;
|
||||
|
||||
LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
|
||||
LL_UI2L(secondsInDays, 60 * 60 * 24 * mExpireDays);
|
||||
LL_MUL(microSecondsInExpireDays, secondsInDays, microSecondsPerSecond);
|
||||
LL_SUB(expirationDate, now, microSecondsInExpireDays);
|
||||
|
||||
// XXX from here until end batch, no early returns!
|
||||
for (mdb_pos pos = count - 1; pos >= 0; --pos) {
|
||||
nsMdbPtr<nsIMdbRow> row(mEnv);
|
||||
err = mTable->PosToRow(mEnv, pos, getter_Acquires(row));
|
||||
NS_ASSERTION(err == 0, "unable to get row");
|
||||
if (err != 0)
|
||||
break;
|
||||
|
||||
NS_ASSERTION(row != nsnull, "no row");
|
||||
if (! row)
|
||||
continue;
|
||||
|
||||
mdbYarn yarn;
|
||||
err = row->AliasCellYarn(mEnv, kToken_LastVisitDateColumn, &yarn);
|
||||
if (err != 0)
|
||||
continue;
|
||||
|
||||
PRInt64 lastVisitedTime;
|
||||
rv = CharsToPRInt64((const char*) yarn.mYarn_Buf, yarn.mYarn_Fill, &lastVisitedTime);
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
|
||||
PRBool expireEntry = LL_CMP(lastVisitedTime, <, expirationDate);
|
||||
if (expireEntry)
|
||||
{
|
||||
if (notify)
|
||||
{
|
||||
// What's the URL? We need to know to properly notify our RDF
|
||||
// observers.
|
||||
err = row->AliasCellYarn(mEnv, kToken_URLColumn, &yarn);
|
||||
if (err != 0)
|
||||
continue;
|
||||
|
||||
nsLiteralCString uri((const char*) yarn.mYarn_Buf, yarn.mYarn_Fill);
|
||||
|
||||
rv = gRDFService->GetResource(nsPromiseFlatCString(uri).get(), getter_AddRefs(resource));
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource");
|
||||
if (NS_FAILED(rv))
|
||||
continue;
|
||||
}
|
||||
// Officially cut the row *now*, before notifying any observers:
|
||||
// that way, any re-entrant calls won't find the row.
|
||||
err = mTable->CutRow(mEnv, row);
|
||||
NS_ASSERTION(err == 0, "couldn't cut row");
|
||||
if (err != 0)
|
||||
continue;
|
||||
|
||||
// XXX possibly avoid leakage
|
||||
err = row->CutAllColumns(mEnv);
|
||||
NS_ASSERTION(err == 0, "couldn't cut all columns");
|
||||
// XXX we'll notify regardless of whether we could successfully
|
||||
// CutAllColumns or not.
|
||||
|
||||
// Notify observers that the row is, er, history.
|
||||
if (notify)
|
||||
NotifyUnassert(kNC_HistoryRoot, kNC_child, resource);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the batch.
|
||||
err = mTable->EndBatchChangeHint(mEnv, &marker);
|
||||
NS_ASSERTION(err == 0, "error ending batch");
|
||||
return ( err == 0) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalHistory::CloseDB()
|
||||
{
|
||||
mdb_err err;
|
||||
|
||||
ExpireEntries(PR_FALSE /* don't notify */);
|
||||
err = Commit(kSessionCommit);
|
||||
|
||||
if (mTable)
|
||||
mTable->CutStrongRef(mEnv);
|
||||
|
||||
if (mStore) {
|
||||
// Commit all changes here.
|
||||
nsMdbPtr<nsIMdbThumb> thumb(mEnv);
|
||||
err = mStore->SessionCommit(mEnv, getter_Acquires(thumb));
|
||||
if (err == 0) {
|
||||
mdb_count total;
|
||||
mdb_count current;
|
||||
mdb_bool done;
|
||||
mdb_bool broken;
|
||||
|
||||
do {
|
||||
err = thumb->DoMore(mEnv, &total, ¤t, &done, &broken);
|
||||
} while ((err == 0) && !broken && !done);
|
||||
}
|
||||
|
||||
if (mStore)
|
||||
mStore->CutStrongRef(mEnv);
|
||||
}
|
||||
|
||||
if (mEnv)
|
||||
mEnv->CloseMdbObject(mEnv /* XXX */);
|
||||
|
@ -1909,6 +2051,30 @@ nsGlobalHistory::NotifyChange(nsIRDFResource* aSource,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalHistory::Observe(nsISupports *aSubject, const PRUnichar *aTopic,
|
||||
const PRUnichar *aSomeData)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
||||
// topics we observe
|
||||
NS_NAMED_LITERAL_STRING(prefChangedTopic, "nsPref:changed");
|
||||
|
||||
// pref changing - update member vars
|
||||
if (prefChangedTopic.Equals(aTopic)) {
|
||||
|
||||
// expiration date
|
||||
nsCAutoString pref; pref.AssignWithConversion(aSomeData);
|
||||
if (pref.Equals(PREF_BROWSER_HISTORY_EXPIRE_DAYS)) {
|
||||
nsCOMPtr<nsIPref> prefs = do_GetService(kPrefCID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
prefs->GetIntPref(PREF_BROWSER_HISTORY_EXPIRE_DAYS, &mExpireDays);
|
||||
}
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
|
|
|
@ -29,10 +29,12 @@
|
|||
#include "nsMdbPtr.h"
|
||||
#include "mdb.h"
|
||||
#include "nsIGlobalHistory.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIRDFDataSource.h"
|
||||
#include "nsIRDFRemoteDataSource.h"
|
||||
#include "nsIRDFService.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -79,7 +81,9 @@ protected:
|
|||
// nsIGlobalHistory interface.
|
||||
//
|
||||
|
||||
class nsGlobalHistory : public nsIGlobalHistory,
|
||||
class nsGlobalHistory : nsSupportsWeakReference,
|
||||
public nsIGlobalHistory,
|
||||
public nsIObserver,
|
||||
public nsIRDFDataSource,
|
||||
public nsIRDFRemoteDataSource
|
||||
{
|
||||
|
@ -90,6 +94,9 @@ public:
|
|||
// nsIGlobalHistory
|
||||
NS_DECL_NSIGLOBALHISTORY
|
||||
|
||||
// nsIObserver - for observing prefs changes
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// nsIRDFDataSource
|
||||
NS_DECL_NSIRDFDATASOURCE
|
||||
|
||||
|
@ -103,12 +110,21 @@ public:
|
|||
protected:
|
||||
|
||||
|
||||
enum eCommitType
|
||||
{
|
||||
kLargeCommit = 0,
|
||||
kSessionCommit = 1,
|
||||
kCompressCommit = 2
|
||||
};
|
||||
|
||||
// Implementation Methods
|
||||
nsresult OpenDB();
|
||||
nsresult OpenExistingFile(nsIMdbFactory *factory, const char *filePath);
|
||||
nsresult OpenNewFile(nsIMdbFactory *factory, const char *filePath);
|
||||
nsresult CreateTokens();
|
||||
nsresult CloseDB();
|
||||
nsresult Commit(eCommitType commitType);
|
||||
nsresult ExpireEntries(PRBool notify);
|
||||
|
||||
PRBool IsURLInHistory(nsIRDFResource* aResource);
|
||||
|
||||
|
@ -152,6 +168,9 @@ protected:
|
|||
mdb_column kToken_VisitCountColumn;
|
||||
mdb_column kToken_NameColumn;
|
||||
|
||||
PRInt32 mExpireDays;
|
||||
PRInt64 mFileSizeOnDisk;
|
||||
|
||||
// pseudo-constants. although the global history really is a
|
||||
// singleton, we'll use this metaphor to be consistent.
|
||||
static PRInt32 gRefCnt;
|
||||
|
|
Загрузка…
Ссылка в новой задаче