diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp index 75e2f63c4472..fb4acaed0324 100644 --- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -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; } diff --git a/docshell/shistory/public/nsISHistoryInternal.idl b/docshell/shistory/public/nsISHistoryInternal.idl index 1dfd5961c6bf..ea35fc2d5886 100644 --- a/docshell/shistory/public/nsISHistoryInternal.idl +++ b/docshell/shistory/public/nsISHistoryInternal.idl @@ -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); }; diff --git a/docshell/shistory/src/nsSHEntry.cpp b/docshell/shistory/src/nsSHEntry.cpp index 9095d1ae26e5..32a8bc87afa2 100644 --- a/docshell/shistory/src/nsSHEntry.cpp +++ b/docshell/shistory/src/nsSHEntry.cpp @@ -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 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 container; + mContentViewer->GetContainer(getter_AddRefs(container)); + nsCOMPtr 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 root; + treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root)); + nsCOMPtr webNav = do_QueryInterface(root); + nsCOMPtr history; + webNav->GetSessionHistory(getter_AddRefs(history)); + nsCOMPtr historyInt = do_QueryInterface(history); + if (!historyInt) + return; + historyInt->EvictExpiredContentViewerForEntry(this); +} + //***************************************************************************** // nsSHEntry: nsIMutationObserver //***************************************************************************** diff --git a/docshell/shistory/src/nsSHEntry.h b/docshell/shistory/src/nsSHEntry.h index 6819e7db6636..95de45337fd9 100644 --- a/docshell/shistory/src/nsSHEntry.h +++ b/docshell/shistory/src/nsSHEntry.h @@ -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 mChildShells; nsCOMPtr mRefreshURIList; nsCOMPtr mOwner; + nsExpirationState mExpirationState; }; #endif /* nsSHEntry_h */ diff --git a/docshell/shistory/src/nsSHistory.cpp b/docshell/shistory/src/nsSHistory.cpp index b3443fff4ad5..fd231b6f9000 100644 --- a/docshell/shistory/src/nsSHistory.cpp +++ b/docshell/shistory/src/nsSHistory.cpp @@ -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 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 entry; trans->GetSHEntry(getter_AddRefs(entry)); nsCOMPtr 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 trans; + GetTransactionAtIndex(startIndex, getter_AddRefs(trans)); + + PRInt32 i; + for (i = startIndex; i <= endIndex; ++i) { + nsCOMPtr 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 diff --git a/docshell/shistory/src/nsSHistory.h b/docshell/shistory/src/nsSHistory.h index 4bd252eb84c0..f5298d702a90 100644 --- a/docshell/shistory/src/nsSHistory.h +++ b/docshell/shistory/src/nsSHistory.h @@ -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();