Bug 578557 - Better handling of expiration with clean history. r=dietrich

This commit is contained in:
Marco Bonardo 2010-07-15 22:38:01 +02:00
Родитель bccfe4ff61
Коммит 3407705e25
2 изменённых файлов: 55 добавлений и 56 удалений

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

@ -5757,33 +5757,12 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
(void)os->RemoveObserver(this, TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING);
#endif
// If shutdown happens in the same scope as the service init, we should
// immediately serve the places-init topic, this way topic observers
// won't try to access the database after xpcom-shutdown.
nsCOMPtr<nsISimpleEnumerator> e;
nsresult rv = os->EnumerateObservers(TOPIC_PLACES_INIT_COMPLETE,
getter_AddRefs(e));
if (NS_SUCCEEDED(rv) && e) {
// This covers a special case that can happen in tests, if the test
// does never interrupt the main thread we could shutdown
// before we fire any notification. That means that if we notify from now
// on, we could init the category cache after xpcom-shutdown and leak.
mCanNotify = false;
nsCOMPtr<nsIObserver> observer;
PRBool loop = PR_TRUE;
while(NS_SUCCEEDED(e->HasMoreElements(&loop)) && loop) {
e->GetNext(getter_AddRefs(observer));
(void)observer->Observe(observer, TOPIC_PLACES_INIT_COMPLETE, nsnull);
}
}
// Notify all Places users that we are about to shutdown. The notification
// is enqueued because there is network work on profile-before-change that
// should run before us.
nsRefPtr<PlacesEvent> shutdownEvent =
new PlacesEvent(TOPIC_PLACES_SHUTDOWN);
rv = NS_DispatchToMainThread(shutdownEvent);
nsresult rv = NS_DispatchToMainThread(shutdownEvent);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
"Unable to shutdown Places: message dispatch failed.");
@ -5797,6 +5776,11 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
}
else if (strcmp(aTopic, TOPIC_PLACES_TEARDOWN) == 0) {
// Don't even try to notify observers from this point on, the category
// cache would init services that could not shutdown correctly or try to
// use our APIs.
mCanNotify = false;
// Operations that are unlikely to create issues to implementers should go
// in global shutdown. Any other thing that must run really late must be
// here instead.

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

@ -254,7 +254,7 @@ const EXPIRATION_QUERIES = {
+ "WHERE v.id IS NULL "
+ "AND v_t.id IS NULL "
+ "AND b.id IS NULL "
+ "AND h.ROWID <> IFNULL(:null_skips_last, last_insert_rowid()) "
+ "AND h.ROWID <> IFNULL(:null_skips_last, (SELECT MAX(ROWID) FROM moz_places_temp)) "
+ "AND SUBSTR(h.url, 1, 6) <> 'place:' "
+ "UNION ALL "
+ "SELECT h.id, h.url, h.last_visit_date AS visit_date, "
@ -266,7 +266,6 @@ const EXPIRATION_QUERIES = {
+ "WHERE v.id IS NULL "
+ "AND v_t.id IS NULL "
+ "AND b.id IS NULL "
+ "AND h.ROWID <> IFNULL(:null_skips_last, last_insert_rowid()) "
+ "AND SUBSTR(h.url, 1, 6) <> 'place:' "
+ "LIMIT :limit_uris",
actions: ACTION.TIMED | ACTION.SHUTDOWN | ACTION.IDLE | ACTION.DEBUG
@ -514,8 +513,7 @@ nsPlacesExpiration.prototype = {
this._prefBranch.removeObserver("", this);
if (this._isIdleObserver)
this._idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
this.expireOnIdle = false;
if (this._timer) {
this._timer.cancel();
@ -527,13 +525,14 @@ nsPlacesExpiration.prototype = {
let self = this;
Services.tm.mainThread.dispatch({
run: function() {
// If we ran a clearHistory recently, we don't want to spend time
// expiring on shutdown, we can just expire session annotations.
// If we ran a clearHistory recently, or database id not dirty, we don't want to spend
// time expiring on shutdown. In such a case just expire session annotations.
let hasRecentClearHistory =
Date.now() - self._lastClearHistoryTime <
SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS * 1000;
let action = hasRecentClearHistory ? ACTION.CLEAN_SHUTDOWN
: ACTION.SHUTDOWN;
let action = hasRecentClearHistory ||
self.status != STATUS.DIRTY ? ACTION.CLEAN_SHUTDOWN
: ACTION.SHUTDOWN;
self._expireWithActionAndLimit(action, LIMIT.LARGE);
self._finalizeInternalStatements();
@ -594,7 +593,8 @@ nsPlacesExpiration.prototype = {
_lastClearHistoryTime: 0,
onClearHistory: function PEX_onClearHistory() {
this._lastClearHistoryTime = Date.now();
// Expire orphans.
// Expire orphans. History status is clean after a clear history.
this.status = STATUS.CLEAN;
this._expireWithActionAndLimit(ACTION.CLEAR_HISTORY, LIMIT.UNLIMITED);
},
@ -610,14 +610,6 @@ nsPlacesExpiration.prototype = {
notify: function PEX_timerCallback()
{
// We don't start observing idle in the constructor because unit tests
// will run with a large idle, and notify it immediately, instead we start
// observing idle at the first timer notification.
if (!this._isIdleObserver) {
this._idle.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
this._isIdleObserver = true;
}
this._expireWithActionAndLimit(ACTION.TIMED, LIMIT.SMALL);
},
@ -632,16 +624,16 @@ nsPlacesExpiration.prototype = {
let row;
while (row = aResultSet.getNextRow()) {
if (!("_expectedResultsCount" in this))
this._expectedResultsCount = row.getResultByName("expected_results");
if (this._expectedResultsCount > 0)
this._expectedResultsCount--;
let uri = Services.io.newURI(row.getResultByName("url"), null, null);
let visitDate = row.getResultByName("visit_date");
let wholeEntry = row.getResultByName("whole_entry");
// Dispatch expiration notifications to history.
this._hsn.notifyOnPageExpired(uri, visitDate, wholeEntry);
this._expectedResultsCount--;
}
},
@ -654,19 +646,13 @@ nsPlacesExpiration.prototype = {
handleCompletion: function PEX_handleCompletion(aReason)
{
if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
// Adapt the aggressivity of partial steps based on the status of history.
// A dirty history will return all the entries we are expecting, while a
// clean one will return less results or no results at all.
this._status = STATUS.UNKNOWN;
if ("_expectedResultsCount" in this) {
let isClean = this._expectedResultsCount > 0;
// Adapt the aggressivity of steps based on the status of history.
// A dirty history will return all the entries we are expecting bringing
// our countdown to zero, while a clean one will not.
this.status = this._expectedResultsCount == 0 ? STATUS.DIRTY
: STATUS.CLEAN;
delete this._expectedResultsCount;
let status = isClean ? STATUS.CLEAN : STATUS.DIRTY;
// If status changes we should restart the timer.
if (this._status != status) {
this._newTimer();
this._status = status;
}
}
// Dispatch a notification that expiration has finished.
@ -679,10 +665,37 @@ nsPlacesExpiration.prototype = {
_urisLimit: PREF_MAX_URIS_NOTSET,
_interval: PREF_INTERVAL_SECONDS_NOTSET,
_status: STATUS.UNKNOWN,
_isIdleObserver: false,
_shuttingDown: false,
_status: STATUS.UNKNOWN,
set status(aNewStatus) {
if (aNewStatus != this._status) {
// If status changes we should restart the timer.
this._status = aNewStatus;
this._newTimer();
// If needed add/remove the cleanup step on idle. We want to expire on
// idle only if history is dirty, to preserve mobile devices batteries.
this.expireOnIdle = aNewStatus == STATUS.DIRTY;
}
return aNewStatus;
},
get status() this._status,
_isIdleObserver: false,
set expireOnIdle(aObserveIdle) {
if (aObserveIdle != this._isIdleObserver) {
if (aObserveIdle && !this._shuttingDown) {
this._idle.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
this._isIdleObserver = true;
}
else {
this._idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
this._isIdleObserver = false;
}
}
return aObserveIdle;
},
_loadPrefs: function PEX__loadPrefs() {
// Get the user's limit, if it was set.
try {
@ -804,7 +817,7 @@ nsPlacesExpiration.prototype = {
baseLimit = this._debugLimit;
break;
}
if (this._status == STATUS.DIRTY && aLimit != LIMIT.DEBUG)
if (this.status == STATUS.DIRTY && aLimit != LIMIT.DEBUG)
baseLimit *= EXPIRE_AGGRESSIVITY_MULTIPLIER;
// Bind the appropriate parameters.
@ -874,7 +887,9 @@ nsPlacesExpiration.prototype = {
{
if (this._timer)
this._timer.cancel();
let interval = (this._status == STATUS.CLEAN) ?
if (this._shuttingDown)
return;
let interval = (this.status == STATUS.CLEAN) ?
this._interval * EXPIRE_AGGRESSIVITY_MULTIPLIER : this._interval;
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);