зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
0157ae6387
Коммит
300a390b7b
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче