Bug 395045. Expire cached content-viewers after they've been unused for 20-30 minutes. r+sr+a=bz

This commit is contained in:
roc+@cs.cmu.edu 2007-09-21 02:19:59 -07:00
parent 3febe0fecf
commit 438effcc42
6 changed files with 140 additions and 4 deletions

View File

@ -80,12 +80,16 @@ Initialize(nsIModule* aSelf)
gInitialized = PR_TRUE;
nsresult rv = nsSHistory::Startup();
NS_ENSURE_SUCCESS(rv, rv);
rv = nsSHEntry::Startup();
return rv;
}
PR_STATIC_CALLBACK(void)
Shutdown(nsIModule* aSelf)
{
nsSHEntry::Shutdown();
gInitialized = PR_FALSE;
}

View File

@ -46,12 +46,13 @@ interface nsIDocShell;
%{C++
#define NS_SHISTORY_INTERNAL_CID \
{0x5b4cba4c, 0xbf67, 0x499a, {0xae, 0x2c, 0x3f, 0x76, 0x65, 0x6f, 0x4a, 0x4e}}
{ 0x9c47c121, 0x1c6e, 0x4d8f, \
{ 0xb9, 0x04, 0x3a, 0xc9, 0x68, 0x11, 0x6e, 0x88 } }
#define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
%}
[scriptable, uuid(df8788d6-c0ed-4517-b47e-c719afc94284)]
[scriptable, uuid(9c47c121-1c6e-4d8f-b904-3ac968116e88)]
interface nsISHistoryInternal: nsISupports
{
/**
@ -98,4 +99,10 @@ interface nsISHistoryInternal: nsISupports
* the previous viewer.
*/
void evictContentViewers(in long previousIndex, in long index);
/**
* Evict the content viewer associated with a session history entry
* that has timed out.
*/
void evictExpiredContentViewerForEntry(in nsISHEntry aEntry);
};

View File

@ -51,14 +51,53 @@
#include "nsIDOMDocument.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsIWebNavigation.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
// Hardcode this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS 30*60
typedef nsExpirationTracker<nsSHEntry,3> HistoryTrackerBase;
class HistoryTracker : public HistoryTrackerBase {
public:
// Expire cached contentviewers after 20-30 minutes in the cache.
HistoryTracker() : HistoryTrackerBase((CONTENT_VIEWER_TIMEOUT_SECONDS/2)*1000) {}
protected:
virtual void NotifyExpired(nsSHEntry* aObj) {
RemoveObject(aObj);
aObj->Expire();
}
};
static HistoryTracker *gHistoryTracker = nsnull;
static PRUint32 gEntryID = 0;
nsresult nsSHEntry::Startup()
{
gHistoryTracker = new HistoryTracker();
return gHistoryTracker ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
void nsSHEntry::Shutdown()
{
delete gHistoryTracker;
gHistoryTracker = nsnull;
}
static void StopTrackingEntry(nsSHEntry *aEntry)
{
if (aEntry->GetExpirationState()->IsTracked()) {
gHistoryTracker->RemoveObject(aEntry);
}
}
//*****************************************************************************
//*** nsSHEntry: Object Management
//*****************************************************************************
nsSHEntry::nsSHEntry()
: mLoadType(0)
, mID(gEntryID++)
@ -109,6 +148,8 @@ ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
nsSHEntry::~nsSHEntry()
{
StopTrackingEntry(this);
// Since we never really remove kids from SHEntrys, we need to null
// out the mParent pointers on all our kids.
mChildren.EnumerateForwards(ClearParentPtr, nsnull);
@ -193,6 +234,8 @@ nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
mDocument->SetShellsHidden(PR_TRUE);
mDocument->AddMutationObserver(this);
}
gHistoryTracker->AddObject(this);
}
return NS_OK;
@ -637,6 +680,7 @@ nsSHEntry::DropPresentationState()
if (mContentViewer)
mContentViewer->ClearHistoryEntry();
StopTrackingEntry(this);
mContentViewer = nsnull;
mSticky = PR_TRUE;
mWindowState = nsnull;
@ -645,6 +689,31 @@ nsSHEntry::DropPresentationState()
mRefreshURIList = nsnull;
}
void
nsSHEntry::Expire()
{
// This entry has timed out. If we still have a content viewer, we need to
// get it evicted.
if (!mContentViewer)
return;
nsCOMPtr<nsISupports> container;
mContentViewer->GetContainer(getter_AddRefs(container));
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
if (!treeItem)
return;
// We need to find the root DocShell since only that object has an
// SHistory and we need the SHistory to evict content viewers
nsCOMPtr<nsIDocShellTreeItem> root;
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
nsCOMPtr<nsISHistory> history;
webNav->GetSessionHistory(getter_AddRefs(history));
nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
if (!historyInt)
return;
historyInt->EvictExpiredContentViewerForEntry(this);
}
//*****************************************************************************
// nsSHEntry: nsIMutationObserver
//*****************************************************************************

View File

@ -58,6 +58,7 @@
#include "nsRect.h"
#include "nsSupportsArray.h"
#include "nsIMutationObserver.h"
#include "nsExpirationTracker.h"
class nsSHEntry : public nsISHEntry,
public nsISHContainer,
@ -75,6 +76,13 @@ public:
void DropPresentationState();
void Expire();
nsExpirationState *GetExpirationState() { return &mExpirationState; }
static nsresult Startup();
static void Shutdown();
private:
~nsSHEntry();
void DocumentMutated();
@ -104,6 +112,7 @@ private:
nsCOMArray<nsIDocShellTreeItem> mChildShells;
nsCOMPtr<nsISupportsArray> mRefreshURIList;
nsCOMPtr<nsISupports> mOwner;
nsExpirationState mExpirationState;
};
#endif /* nsSHEntry_h */

View File

@ -789,11 +789,17 @@ nsSHistory::EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
}
endIndex = PR_MIN(mLength, aFromIndex + gHistoryMaxViewers);
}
EvictContentViewersInRange(startIndex, endIndex);
}
void
nsSHistory::EvictContentViewersInRange(PRInt32 aStart, PRInt32 aEnd)
{
nsCOMPtr<nsISHTransaction> trans;
GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
GetTransactionAtIndex(aStart, getter_AddRefs(trans));
for (PRInt32 i = startIndex; i < endIndex; ++i) {
for (PRInt32 i = aStart; i < aEnd; ++i) {
nsCOMPtr<nsISHEntry> entry;
trans->GetSHEntry(getter_AddRefs(entry));
nsCOMPtr<nsIContentViewer> viewer;
@ -941,6 +947,46 @@ nsSHistory::EvictGlobalContentViewer()
} // while shouldTryEviction
}
NS_IMETHODIMP
nsSHistory::EvictExpiredContentViewerForEntry(nsISHEntry *aEntry)
{
PRInt32 startIndex = PR_MAX(0, mIndex - gHistoryMaxViewers);
PRInt32 endIndex = PR_MIN(mLength - 1,
mIndex + gHistoryMaxViewers);
nsCOMPtr<nsISHTransaction> trans;
GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
PRInt32 i;
for (i = startIndex; i <= endIndex; ++i) {
nsCOMPtr<nsISHEntry> entry;
trans->GetSHEntry(getter_AddRefs(entry));
if (entry == aEntry)
break;
nsISHTransaction *temp = trans;
temp->GetNext(getter_AddRefs(trans));
}
if (i > endIndex)
return NS_OK;
NS_ASSERTION(i != mIndex, "How did the current session entry expire?");
if (i == mIndex)
return NS_OK;
// We evict content viewers for the expired entry and any other entries that
// we would have to go through the expired entry to get to (i.e. the entries
// that have the expired entry between them and the current entry). Those
// other entries should have timed out already, actually, but this is just
// to be on the safe side.
if (i < mIndex) {
EvictContentViewersInRange(startIndex, i + 1);
} else {
EvictContentViewersInRange(i, endIndex + 1);
}
return NS_OK;
}
// Evicts all content viewers in all history objects. This is very
// inefficient, because it requires a linear search through all SHistory
// objects for each viewer to be evicted. However, this method is called

View File

@ -99,6 +99,7 @@ protected:
nsresult PrintHistory();
#endif
void EvictContentViewersInRange(PRInt32 aStartIndex, PRInt32 aEndIndex);
void EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
static void EvictGlobalContentViewer();
static void EvictAllContentViewers();