Add a global limit to the number of cached content viewers that scales with the amount of physical memory. Patch by Marria Nazif <marria@gmail.com>. Bug 292965, r=biesi, sr=me.

This commit is contained in:
bryner%brianryner.com 2005-09-14 03:38:40 +00:00
Родитель 0157ae6387
Коммит 300a390b7b
15 изменённых файлов: 494 добавлений и 77 удалений

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

@ -214,7 +214,6 @@ pref("browser.search.update", true);
pref("browser.history.grouping", "day");
pref("browser.sessionhistory.max_entries", 50);
pref("browser.sessionhistory.max_viewers", 3);
// handle external links
// 0=default window, 1=current window/tab, 2=new window, 3=new tab in most recent window

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

@ -127,6 +127,3 @@ pref("browser.tabs.loadInBackground", false);
// use the html network errors rather than sheets
pref("browser.xul.error_pages.enabled", true);
// turn on bfcache optimizations
pref("browser.sessionhistory.max_viewers", 3);

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

@ -259,6 +259,8 @@ nsDocShell::nsDocShell():
mMarginHeight(0),
mItemType(typeContent),
mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
mPreviousTransIndex(-1),
mLoadedTransIndex(-1),
mEditorData(nsnull),
mTreeOwner(nsnull),
mChromeEventHandler(nsnull)
@ -1707,6 +1709,20 @@ nsDocShell::SetUseErrorPages(PRBool aUseErrorPages)
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
{
*aPreviousTransIndex = mPreviousTransIndex;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
{
*aLoadedTransIndex = mLoadedTransIndex;
return NS_OK;
}
//*****************************************************************************
// nsDocShell::nsIDocShellTreeItem
//*****************************************************************************
@ -2634,6 +2650,19 @@ nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
* for this subframe in the CloneAndReplace function.
*/
// In this case, we will end up calling AddEntry, which increases the
// current index by 1
nsCOMPtr<nsISHistory> rootSH;
GetRootSessionHistory(getter_AddRefs(rootSH));
if (rootSH) {
rootSH->GetIndex(&mPreviousTransIndex);
mLoadedTransIndex = mPreviousTransIndex + 1;
#ifdef DEBUG_PAGE_CACHE
printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
mLoadedTransIndex);
#endif
}
nsresult rv;
nsCOMPtr<nsIDocShellHistory> parent =
do_QueryInterface(GetAsSupports(mParent), &rv);
@ -4854,10 +4883,15 @@ nsDocShell::CanSavePresentation(PRUint32 aLoadType,
// Avoid doing the work of saving the presentation state in the case where
// the content viewer cache is disabled.
PRInt32 maxViewers = 0;
mPrefs->GetIntPref("browser.sessionhistory.max_viewers", &maxViewers);
if (maxViewers == 0)
nsCOMPtr<nsISHistory> rootSH;
GetRootSessionHistory(getter_AddRefs(rootSH));
if (rootSH) {
nsCOMPtr<nsISHistoryInternal> shistInt(do_QueryInterface(rootSH));
PRInt32 maxViewers;
shistInt->GetHistoryMaxTotalViewers(&maxViewers);
if (maxViewers == 0)
return PR_FALSE;
}
// Don't cache the content viewer if we're in a subframe and the subframe
// pref is disabled.
@ -5210,7 +5244,13 @@ nsDocShell::RestoreFromHistory()
GetRootSessionHistory(getter_AddRefs(rootSH));
if (rootSH) {
nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
rootSH->GetIndex(&mPreviousTransIndex);
hist->UpdateIndex();
rootSH->GetIndex(&mLoadedTransIndex);
#ifdef DEBUG_PAGE_CACHE
printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
mLoadedTransIndex);
#endif
}
// Rather than call Embed(), we will retrieve the viewer from the session
@ -5278,7 +5318,7 @@ nsDocShell::RestoreFromHistory()
if (mContentViewer) {
mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull);
mContentViewer->Destroy();
viewer->SetPreviousViewer(mContentViewer);
}
mContentViewer.swap(viewer);
@ -7213,8 +7253,15 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel,
// SH.
if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
if (shInternal)
if (shInternal) {
rootSH->GetIndex(&mPreviousTransIndex);
shInternal->UpdateIndex();
rootSH->GetIndex(&mLoadedTransIndex);
#ifdef DEBUG_PAGE_CACHE
printf("Previous index: %d, Loaded index: %d\n\n",
mPreviousTransIndex, mLoadedTransIndex);
#endif
}
}
PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
aFireOnLocationChange);
@ -7424,7 +7471,13 @@ nsDocShell::AddToSessionHistory(nsIURI * aURI,
nsCOMPtr<nsISHistoryInternal>
shPrivate(do_QueryInterface(mSessionHistory));
NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
mSessionHistory->GetIndex(&mPreviousTransIndex);
rv = shPrivate->AddEntry(entry, shouldPersist);
mSessionHistory->GetIndex(&mLoadedTransIndex);
#ifdef DEBUG_PAGE_CACHE
printf("Previous index: %d, Loaded index: %d\n\n",
mPreviousTransIndex, mLoadedTransIndex);
#endif
}
}
else {

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

@ -590,6 +590,11 @@ protected:
// Somebody give me better name
nsCOMPtr<nsISHEntry> mLSHE;
// Index into the SHTransaction list, indicating the previous and current
// transaction at the time that this DocShell begins to load
PRInt32 mPreviousTransIndex;
PRInt32 mLoadedTransIndex;
// Editor stuff
nsDocShellEditorData* mEditorData; // editor data, if any

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

@ -66,7 +66,7 @@ interface nsISHEntry;
interface nsILayoutHistoryState;
interface nsISecureBrowserUI;
[scriptable, uuid(cfe93f8d-bd92-4e77-a9b7-c37296fe0986)]
[scriptable, uuid(9f0c7461-b9a4-47f6-b88c-421dce1bce66)]
interface nsIDocShell : nsISupports
{
/**
@ -387,5 +387,11 @@ interface nsIDocShell : nsISupports
/* attribute to access whether error pages are enabled */
attribute boolean useErrorPages;
/* keeps track of the previous SHTransaction index and the current
SHTransaction index at the time that the doc shell begins to load.
Used for ContentViewer eviction */
readonly attribute long previousTransIndex;
readonly attribute long loadedTransIndex;
};

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

@ -898,8 +898,15 @@ nsresult nsWebShell::EndPageLoad(nsIWebProgress *aProgress,
if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
if (shInternal)
shInternal->UpdateIndex();
if (shInternal) {
rootSH->GetIndex(&mPreviousTransIndex);
shInternal->UpdateIndex();
rootSH->GetIndex(&mLoadedTransIndex);
#ifdef DEBUG_PAGE_CACHE
printf("Previous index: %d, Loaded index: %d\n\n",
mPreviousTransIndex, mLoadedTransIndex);
#endif
}
}
// Make it look like we really did honestly finish loading the

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

@ -62,6 +62,29 @@
#include "nsGlobalHistoryAdapter.h"
#include "nsGlobalHistory2Adapter.h"
static PRBool gInitialized = PR_FALSE;
// The one time initialization for this module
// static
PR_STATIC_CALLBACK(nsresult)
Initialize(nsIModule* aSelf)
{
NS_PRECONDITION(!gInitialized, "docshell module already initialized");
if (gInitialized) {
return NS_OK;
}
gInitialized = PR_TRUE;
nsresult rv = nsSHistory::Startup();
return rv;
}
PR_STATIC_CALLBACK(void)
Shutdown(nsIModule* aSelf)
{
gInitialized = PR_FALSE;
}
// docshell
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWebShell, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDefaultURIFixup)
@ -83,7 +106,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsInternetConfigService)
// session history
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHEntry)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHTransaction)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSHistory, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHistory)
// Currently no-one is instantiating docshell's directly because
// nsWebShell is still our main "shell" class. nsWebShell is a subclass
@ -154,5 +177,5 @@ static const nsModuleComponentInfo gDocShellModuleInfo[] = {
// "docshell provider" to illustrate that this thing really *should*
// be dispensing docshells rather than webshells.
NS_IMPL_NSGETMODULE(docshell_provider, gDocShellModuleInfo)
NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(docshell_provider, gDocShellModuleInfo,
Initialize, Shutdown)

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

@ -55,7 +55,7 @@ struct nsRect;
%}
[ref] native nsRect(nsRect);
[scriptable, uuid(a19c4489-4a71-42e1-b150-61366547030d)]
[scriptable, uuid(542a98b9-2889-4922-aaf4-02b6056f4136)]
interface nsISHEntry : nsIHistoryEntry
{
/** URI for the document */
@ -177,6 +177,11 @@ interface nsISHEntry : nsIHistoryEntry
/** Attribute that indicates if this entry is for a subframe navigation */
void setIsSubFrame(in boolean aFlag);
/** Return any content viewer present in or below this node in the
nsSHEntry tree. This will differ from contentViewer in the case
where a child nsSHEntry has the content viewer for this tree. */
nsIContentViewer getAnyContentViewer(out nsISHEntry ownerEntry);
};

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

@ -51,7 +51,7 @@ interface nsIDocShell;
#define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
%}
[scriptable, uuid(DD335421-B8B8-11d3-BDC8-0050040A9B44)]
[scriptable, uuid(494fac3c-64f4-41b8-b209-b4ada899613b)]
interface nsISHistoryInternal: nsISupports
{
/**
@ -91,10 +91,19 @@ interface nsISHistoryInternal: nsISupports
readonly attribute nsISHistoryListener listener;
/**
* Evict content viewers until the number of content viewers is no more than
* browser.sessionhistory.max_viewers. This is done automatically by
* updateIndex(), but should be called explicitly if a new history entry
* is added and later has a content viewer set.
* Evict content viewers until the number of content viewers per tab
* is no more than gHistoryMaxViewers. Also, count
* total number of content viewers globally and evict one if we are over
* our total max. This is always called in Show(), after we destroy
* the previous viewer.
*/
void evictContentViewers();
void evictContentViewers(in long previousIndex, in long index);
/**
* Max number of total cached content viewers. If the pref
* browser.sessionhistory.max_total_viewers is negative, then
* this value is calculated based on the total amount of memory.
* Otherwise, it comes straight from the pref.
*/
readonly attribute long historyMaxTotalViewers;
};

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

@ -199,6 +199,38 @@ nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetAnyContentViewer(nsISHEntry **aOwnerEntry,
nsIContentViewer **aResult)
{
// Find a content viewer in the root node or any of its children,
// assuming that there is only one content viewer total in any one
// nsSHEntry tree
GetContentViewer(aResult);
if (*aResult) {
#ifdef DEBUG_PAGE_CACHE
printf("Found content viewer\n");
#endif
*aOwnerEntry = this;
NS_ADDREF(*aOwnerEntry);
return NS_OK;
}
// The root SHEntry doesn't have a ContentViewer, so check child nodes
for (PRInt32 i = 0; i < mChildren.Count(); i++) {
nsISHEntry* child = mChildren[i];
if (child) {
#ifdef DEBUG_PAGE_CACHE
printf("Evaluating SHEntry child %d\n", i);
#endif
child->GetAnyContentViewer(aOwnerEntry, aResult);
if (*aResult) {
return NS_OK;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetSticky(PRBool aSticky)
{

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

@ -56,12 +56,24 @@
#include "nsIPrefService.h"
#include "nsIURI.h"
#include "nsIContentViewer.h"
#include "nsIPrefBranch2.h"
#include "prclist.h"
// For calculating max history entries and max cachable contentviewers
#include "nspr.h"
#include <math.h> // for log()
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
#define PREF_SHISTORY_VIEWERS "browser.sessionhistory.max_viewers"
#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
static PRInt32 gHistoryMaxSize = 50;
static PRInt32 gHistoryMaxViewers = 0;
// Max viewers allowed per SHistory objects
static const PRInt32 gHistoryMaxViewers = 3;
// List of all SHistory objects, used for content viewer cache eviction
static PRCList gSHistoryList;
// Max viewers allowed total, across all SHistory objects - negative default
// means we will calculate how many viewers to cache based on total memory
PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
enum HistCmd{
HIST_CMD_BACK,
@ -70,17 +82,58 @@ enum HistCmd{
HIST_CMD_RELOAD
} ;
//*****************************************************************************
//*** nsSHistoryPrefObserver
//*****************************************************************************
class nsSHistoryPrefObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
nsSHistoryPrefObserver() {}
protected:
~nsSHistoryPrefObserver() {}
};
NS_IMPL_ISUPPORTS1(nsSHistoryPrefObserver, nsIObserver)
NS_IMETHODIMP
nsSHistoryPrefObserver::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
prefs->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
&nsSHistory::sHistoryMaxTotalViewers);
if (nsSHistory::sHistoryMaxTotalViewers < 0) {
nsSHistory::sHistoryMaxTotalViewers = nsSHistory::GetMaxTotalViewers();
}
nsSHistory::EvictGlobalContentViewer();
}
return NS_OK;
}
//*****************************************************************************
//*** nsSHistory: Object Management
//*****************************************************************************
nsSHistory::nsSHistory() : mListRoot(nsnull), mIndex(-1), mLength(0), mRequestedIndex(-1)
{
// Add this new SHistory object to the list
PR_APPEND_LINK(this, &gSHistoryList);
}
nsSHistory::~nsSHistory()
{
// Remove this SHistory object from the list
PR_REMOVE_LINK(this);
}
//*****************************************************************************
@ -101,11 +154,71 @@ NS_INTERFACE_MAP_END
// nsSHistory: nsISHistory
//*****************************************************************************
/*
* Init method to get pref settings
*/
NS_IMETHODIMP
nsSHistory::Init()
nsSHistory::GetHistoryMaxTotalViewers(PRInt32 *max)
{
*max = sHistoryMaxTotalViewers;
return NS_OK;
}
// static
PRUint32
nsSHistory::GetMaxTotalViewers()
{
// Calculate an estimate of how many ContentViewers we should cache based
// on RAM. This assumes that the average ContentViewer is 4MB (conservative)
// and caps the max at 8 ContentViewers
//
// TODO: Should we split the cache memory betw. ContentViewer caching and
// nsCacheService?
//
// RAM ContentViewers
// -----------------------
// 32 Mb 0
// 64 Mb 1
// 128 Mb 2
// 256 Mb 3
// 512 Mb 5
// 1024 Mb 8
// 2048 Mb 8
// 4096 Mb 8
PRUint64 bytes = PR_GetPhysicalMemorySize();
if (LL_IS_ZERO(bytes))
return 0;
// Conversion from unsigned int64 to double doesn't work on all platforms.
// We need to truncate the value at LL_MAXINT to make sure we don't
// overflow.
if (LL_CMP(bytes, >, LL_MAXINT))
bytes = LL_MAXINT;
PRUint64 kbytes;
LL_SHR(kbytes, bytes, 10);
double kBytesD;
LL_L2D(kBytesD, (PRInt64) kbytes);
// This is essentially the same calculation as for nsCacheService,
// except that we divide the final memory calculation by 4, since
// we assume each ContentViewer takes on average 4MB
PRUint32 viewers = 0;
double x = log(kBytesD)/log(2.0) - 14;
if (x > 0) {
viewers = (PRUint32)(x * x - x + 2.001); // add .001 for rounding
viewers /= 4;
}
// Cap it off at 8 max
if (viewers > 8) {
viewers = 8;
}
return viewers;
}
// static
nsresult
nsSHistory::Startup()
{
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
@ -118,16 +231,30 @@ nsSHistory::Init()
if (defaultBranch) {
defaultBranch->GetIntPref(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
}
// The size of the content viewer cache does not suffer from this problem,
// so we allow it to be overridden by user prefs.
nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
if (branch)
branch->GetIntPref(PREF_SHISTORY_VIEWERS, &gHistoryMaxViewers);
// Allow the user to override the max total number of cached viewers,
// but keep the per SHistory cached viewer limit constant
nsCOMPtr<nsIPrefBranch2> branch = do_QueryInterface(prefs);
if (branch) {
branch->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
&sHistoryMaxTotalViewers);
nsSHistoryPrefObserver* obs = new nsSHistoryPrefObserver();
if (!obs) {
return NS_ERROR_OUT_OF_MEMORY;
}
branch->AddObserver(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
obs, PR_FALSE);
}
}
// If the pref is negative, that means we calculate how many viewers
// we think we should cache, based on total memory
if (sHistoryMaxTotalViewers < 0) {
sHistoryMaxTotalViewers = GetMaxTotalViewers();
}
return NS_OK;
}
// Initialize the global list of all SHistory objects
PR_INIT_CLIST(&gSHistoryList);
}
/* Add an entry to the History list at mIndex and
* increment the index to point to the new entry
@ -222,7 +349,6 @@ nsSHistory::GetEntryAtIndex(PRInt32 aIndex, PRBool aModifyIndex, nsISHEntry** aR
if (NS_SUCCEEDED(rv) && (*aResult)) {
// Set mIndex to the requested index, if asked to do so..
if (aModifyIndex) {
EvictContentViewers(mIndex, aIndex);
mIndex = aIndex;
}
} //entry
@ -493,10 +619,12 @@ nsSHistory::GetListener(nsISHistoryListener ** aListener)
}
NS_IMETHODIMP
nsSHistory::EvictContentViewers()
nsSHistory::EvictContentViewers(PRInt32 aPreviousIndex, PRInt32 aIndex)
{
// This is called after a new entry has been appended to the end of the list.
EvictContentViewers(mIndex - 1, mIndex);
// Check our per SHistory object limit in the currently navigated SHistory
EvictWindowContentViewers(aPreviousIndex, aIndex);
// Check our total limit across all SHistory objects
EvictGlobalContentViewer();
return NS_OK;
}
@ -608,43 +736,60 @@ nsSHistory::Reload(PRUint32 aReloadFlags)
}
void
nsSHistory::EvictContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
nsSHistory::EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
{
// To enforce the limit on cached content viewers, we need to release all
// of the content viewers that are no longer in the "window" that now
// ends/begins at aToIndex.
// To enforce the per SHistory object limit on cached content viewers, we
// need to release all of the content viewers that are no longer in the
// "window" that now ends/begins at aToIndex.
// This can happen on the first load of a page in a particular window
if (aFromIndex < 0 || aToIndex < 0) {
return;
}
// These indices give the range of SHEntries whose content viewers will be
// evicted
PRInt32 startIndex, endIndex;
if (aToIndex > aFromIndex) { // going forward
endIndex = aToIndex - gHistoryMaxViewers;
if (endIndex <= 0) {
return;
}
startIndex = PR_MAX(0, aFromIndex - gHistoryMaxViewers);
endIndex = PR_MAX(0, aToIndex - gHistoryMaxViewers);
} else { // going backward
startIndex = PR_MIN(mLength - 1, aToIndex + gHistoryMaxViewers);
endIndex = PR_MIN(mLength - 1, aFromIndex + gHistoryMaxViewers);
startIndex = aToIndex + gHistoryMaxViewers + 1;
if (startIndex >= mLength) {
return;
}
endIndex = PR_MIN(mLength, aFromIndex + gHistoryMaxViewers);
}
nsCOMPtr<nsISHTransaction> trans;
GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
for (PRInt32 i = startIndex; trans && i < endIndex; ++i) {
for (PRInt32 i = startIndex; i < endIndex; ++i) {
nsCOMPtr<nsISHEntry> entry;
trans->GetSHEntry(getter_AddRefs(entry));
nsCOMPtr<nsIContentViewer> viewer;
entry->GetContentViewer(getter_AddRefs(viewer));
nsCOMPtr<nsISHEntry> ownerEntry;
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
getter_AddRefs(viewer));
if (viewer) {
NS_ASSERTION(ownerEntry,
"ContentViewer exists but its SHEntry is null");
#ifdef DEBUG_PAGE_CACHE
nsCOMPtr<nsIURI> uri;
entry->GetURI(getter_AddRefs(uri));
ownerEntry->GetURI(getter_AddRefs(uri));
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
printf("Evicting content viewer: %s\n", spec.get());
printf("per SHistory limit: evicting content viewer: %s\n", spec.get());
#endif
viewer->Destroy();
entry->SetContentViewer(nsnull);
entry->SyncPresentationState();
ownerEntry->SetContentViewer(nsnull);
ownerEntry->SyncPresentationState();
}
nsISHTransaction *temp = trans;
@ -652,15 +797,125 @@ nsSHistory::EvictContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
}
}
// static
void
nsSHistory::EvictGlobalContentViewer()
{
// true until the total number of content viewers is <= total max
// The usual case is that we only need to evict one content viewer.
// However, if somebody resets the pref value, we might occasionally
// need to evict more than one.
PRBool shouldTryEviction = PR_TRUE;
while (shouldTryEviction) {
// Walk through our list of SHistory objects, looking for content
// viewers in the possible active window of all of the SHEntry objects.
// Keep track of the SHEntry object that has a ContentViewer and is
// farthest from the current focus in any SHistory object. The
// ContentViewer associated with that SHEntry will be evicted
PRInt32 distanceFromFocus = 0;
nsCOMPtr<nsISHEntry> evictFromSHE;
nsCOMPtr<nsIContentViewer> evictViewer;
PRInt32 totalContentViewers = 0;
nsSHistory* shist = NS_STATIC_CAST(nsSHistory*,
PR_LIST_HEAD(&gSHistoryList));
while (shist != &gSHistoryList) {
// Calculate the window of SHEntries that could possibly have a content
// viewer. There could be up to gHistoryMaxViewers content viewers,
// but we don't know whether they are before or after the mIndex position
// in the SHEntry list. Just check both sides, to be safe.
PRInt32 startIndex = PR_MAX(0, shist->mIndex - gHistoryMaxViewers);
PRInt32 endIndex = PR_MIN(shist->mLength - 1,
shist->mIndex + gHistoryMaxViewers);
nsCOMPtr<nsISHTransaction> trans;
shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
for (PRInt32 i = startIndex; i <= endIndex; ++i) {
nsCOMPtr<nsISHEntry> entry;
trans->GetSHEntry(getter_AddRefs(entry));
nsCOMPtr<nsIContentViewer> viewer;
nsCOMPtr<nsISHEntry> ownerEntry;
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
getter_AddRefs(viewer));
#ifdef DEBUG_PAGE_CACHE
nsCOMPtr<nsIURI> uri;
if (ownerEntry) {
ownerEntry->GetURI(getter_AddRefs(uri));
} else {
entry->GetURI(getter_AddRefs(uri));
}
nsCAutoString spec;
if (uri) {
uri->GetSpec(spec);
printf("Considering for eviction: %s\n", spec.get());
}
#endif
// This SHEntry has a ContentViewer, so check how far away it is from
// the currently used SHEntry within this SHistory object
if (viewer) {
PRInt32 distance = PR_ABS(shist->mIndex - i);
#ifdef DEBUG_PAGE_CACHE
printf("Has a cached content viewer: %s\n", spec.get());
printf("mIndex: %d i: %d\n", shist->mIndex, i);
#endif
totalContentViewers++;
if (distance > distanceFromFocus) {
#ifdef DEBUG_PAGE_CACHE
printf("Choosing as new eviction candidate: %s\n", spec.get());
#endif
distanceFromFocus = distance;
evictFromSHE = ownerEntry;
evictViewer = viewer;
}
}
nsISHTransaction* temp = trans;
temp->GetNext(getter_AddRefs(trans));
}
shist = NS_STATIC_CAST(nsSHistory*, PR_NEXT_LINK(shist));
}
#ifdef DEBUG_PAGE_CACHE
printf("Distance from focus: %d\n", distanceFromFocus);
printf("Total max viewers: %d\n", sHistoryMaxTotalViewers);
printf("Total number of viewers: %d\n", totalContentViewers);
#endif
if (totalContentViewers > sHistoryMaxTotalViewers && evictViewer) {
#ifdef DEBUG_PAGE_CACHE
nsCOMPtr<nsIURI> uri;
evictFromSHE->GetURI(getter_AddRefs(uri));
nsCAutoString spec;
if (uri) {
uri->GetSpec(spec);
printf("Evicting content viewer: %s\n", spec.get());
}
#endif
evictViewer->Destroy();
evictFromSHE->SetContentViewer(nsnull);
evictFromSHE->SyncPresentationState();
// If we only needed to evict one content viewer, then we are done.
// Otherwise, continue evicting until we reach the max total limit.
if (totalContentViewers - sHistoryMaxTotalViewers == 1) {
shouldTryEviction = PR_FALSE;
}
} else {
// couldn't find a content viewer to evict, so we are done
shouldTryEviction = PR_FALSE;
}
} // while shouldTryEviction
}
NS_IMETHODIMP
nsSHistory::UpdateIndex()
{
// Update the actual index with the right value.
if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
// We've just finished a history navigation (back or forward), so enforce
// the max number of content viewers.
EvictContentViewers(mIndex, mRequestedIndex);
mIndex = mRequestedIndex;
}

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

@ -52,10 +52,16 @@
#include "nsISimpleEnumerator.h"
#include "nsISHistoryListener.h"
#include "nsIHistoryEntry.h"
#include "nsIObserver.h"
// Needed to maintain global list of all SHistory objects
#include "prclist.h"
class nsIDocShell;
class nsSHEnumerator;
class nsSHistory: public nsISHistory,
class nsSHistoryPrefObserver;
class nsSHistory: public PRCList,
public nsISHistory,
public nsISHistoryInternal,
public nsIWebNavigation
{
@ -67,11 +73,17 @@ public:
NS_DECL_NSISHISTORYINTERNAL
NS_DECL_NSIWEBNAVIGATION
NS_IMETHOD Init();
// One time initialization method called upon docshell module construction
static nsresult Startup();
// Calculates a max number of total
// content viewers to cache, based on amount of total memory
static PRUint32 GetMaxTotalViewers();
protected:
virtual ~nsSHistory();
friend class nsSHEnumerator;
friend class nsSHistoryPrefObserver;
// Could become part of nsIWebNavigation
NS_IMETHOD GetEntryAtIndex(PRInt32 aIndex, PRBool aModifyIndex, nsISHEntry** aResult);
@ -85,7 +97,8 @@ protected:
nsresult PrintHistory();
#endif
void EvictContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
void EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
static void EvictGlobalContentViewer();
protected:
nsCOMPtr<nsISHTransaction> mListRoot;
@ -96,6 +109,9 @@ protected:
nsWeakPtr mListener;
// Weak reference. Do not refcount this.
nsIDocShell * mRootDocShell;
// Max viewers allowed total, across all SHistory objects
static PRInt32 sHistoryMaxTotalViewers;
};
//*****************************************************************************
//*** nsSHEnumerator: Object Management
@ -117,6 +133,4 @@ private:
nsSHistory * mSHistory;
};
#endif /* nsSHistory */

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

@ -1370,20 +1370,6 @@ DocumentViewerImpl::Destroy()
mSHEntry = nsnull;
// If we put ourselves into session history, make sure there aren't
// too many content viewers around. Note: if max_viewers is set to 0,
// this can reenter Destroy() and dispose of this content viewer!
nsCOMPtr<nsIWebNavigation> webNav = do_QueryReferent(mContainer);
if (webNav) {
nsCOMPtr<nsISHistory> history;
webNav->GetSessionHistory(getter_AddRefs(history));
nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
if (historyInt) {
historyInt->EvictContentViewers();
}
}
// Break the link from the document/presentation to the docshell, so that
// link traversals cannot affect the currently-loaded document.
// When the presentation is restored, Open() and InitInternal() will reset
@ -1720,6 +1706,30 @@ DocumentViewerImpl::Show(void)
nsCOMPtr<nsIContentViewer> prevViewer(mPreviousViewer);
mPreviousViewer = nsnull;
prevViewer->Destroy();
// Make sure we don't have too many cached ContentViewers
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryReferent(mContainer);
if (treeItem) {
// 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) {
PRInt32 prevIndex,loadedIndex;
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
docShell->GetPreviousTransIndex(&prevIndex);
docShell->GetLoadedTransIndex(&loadedIndex);
#ifdef DEBUG_PAGE_CACHE
printf("About to evict content viewers: prev=%d, loaded=%d\n",
prevIndex, loadedIndex);
#endif
historyInt->EvictContentViewers(prevIndex, loadedIndex);
}
}
}
if (mWindow) {

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

@ -62,6 +62,9 @@ pref("browser.cache.memory.enable", true);
pref("browser.cache.disk_cache_ssl", false);
// 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
pref("browser.cache.check_doc_frequency", 3);
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);
pref("browser.display.use_document_fonts", 1); // 0 = never, 1 = quick, 2 = always
pref("browser.display.use_document_colors", true);

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

@ -113,7 +113,6 @@ pref("browser.search.param.Google.1.default", "chrome://navigator/content/search
pref("browser.history.grouping", "day");
pref("browser.sessionhistory.max_entries", 50);
pref("browser.sessionhistory.max_viewers", 3);
// Tabbed browser
pref("browser.tabs.loadDivertedInBackground", false);