Add support for caching content viewers in session history to speed up back/forward (bug 274784). This initial landing has the feature disabled by default; set browser.sessionhistory.max_viewers to the maximum number of pages to cache to enable the feature. r=bzbarsky, sr/a=brendan.

This commit is contained in:
bryner%brianryner.com 2005-08-18 11:17:00 +00:00
Родитель 7e7578bb1f
Коммит e3bc99eb80
8 изменённых файлов: 383 добавлений и 100 удалений

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

@ -53,30 +53,30 @@ interface nsIURI;
interface nsIHistoryEntry : nsISupports
{
/**
* A readonly property that returns the URI
* of the current entry. The object returned is
* of type nsIURI
*/
readonly attribute nsIURI URI;
/**
* A readonly property that returns the URI
* of the current entry. The object returned is
* of type nsIURI
*/
readonly attribute nsIURI URI;
/**
* A readonly property that returns the title
* of the current entry. The object returned
* is a encoded string
*/
readonly attribute wstring title;
/**
* A readonly property that returns the title
* of the current entry. The object returned
* is a encoded string
*/
readonly attribute wstring title;
/**
* A readonly property that returns a boolean
* flag which indicates if the entry was created as a
* result of a subframe navigation. This flag will be
* 'false' when a frameset page is visited for
* the first time. This flag will be 'true' for all
* history entries created as a result of a subframe
* navigation.
*/
readonly attribute boolean isSubFrame;
/**
* A readonly property that returns a boolean
* flag which indicates if the entry was created as a
* result of a subframe navigation. This flag will be
* 'false' when a frameset page is visited for
* the first time. This flag will be 'true' for all
* history entries created as a result of a subframe
* navigation.
*/
readonly attribute boolean isSubFrame;
};

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

@ -42,88 +42,145 @@
* hold all information required to recreate the document from history
*
*/
#include "nsISupports.idl"
#include "nsIURI.idl"
#include "nsIInputStream.idl"
#include "nsIHistoryEntry.idl"
interface nsILayoutHistoryState;
interface nsIDOMDocument;
interface nsIContentViewer;
interface nsIURI;
interface nsIInputStream;
interface nsIDocShellTreeItem;
interface nsISecureBrowserUIState;
interface nsISupportsArray;
%{C++
struct nsRect;
%}
[ref] native nsRect(nsRect);
[scriptable, uuid(6b596e1f-a3bd-40f9-a7ee-ab3edc7f9960)]
interface nsISHEntry : nsISupports
[scriptable, uuid(e47bf412-3bc2-4306-a82f-ea2bdf950432)]
interface nsISHEntry : nsIHistoryEntry
{
/** URI for the document */
void setURI(in nsIURI aURI);
/** URI for the document */
void SetURI(in nsIURI aURI);
/** Referrer URI */
attribute nsIURI referrerURI;
/** Referrer URI */
attribute nsIURI referrerURI;
/** Content viewer, for fast restoration of presentation */
attribute nsIContentViewer contentViewer;
/** DOM Document */
attribute nsIDOMDocument document;
/** Whether the content viewer is marked "sticky" */
attribute boolean sticky;
/** Title for the document */
void SetTitle(in wstring aTitle);
/** Saved state of the global window object */
attribute nsISupports windowState;
/** Post Data for the document */
attribute nsIInputStream postData;
/**
* Saved position and dimensions of the content viewer; we must adjust the
* root view's widget accordingly if this has changed when the presentation
* is restored.
*/
[noscript] void getViewerBounds(in nsRect bounds);
[noscript] void setViewerBounds([const] in nsRect bounds);
/** LayoutHistoryState for scroll position and form values */
attribute nsILayoutHistoryState layoutHistoryState;
/**
* Saved child docshells corresponding to contentViewer. There are weak
* references since it's assumed that the content viewer's document has
* an owning reference to the subdocument for each shell. The child shells
* are restored as children of the parent docshell, in this order, when the
* parent docshell restores a saved presentation.
*/
/** parent of this entry */
attribute nsISHEntry parent;
/** Append a child shell to the end of our list. */
void addChildShell(in nsIDocShellTreeItem shell);
/**
* The loadType for this entry. This is typically loadHistory except
* when reload is pressed, it has the appropriate reload flag
*/
attribute unsigned long loadType;
/**
* Get the child shell at |index|; returns null if |index| is out of bounds.
*/
nsIDocShellTreeItem childShellAt(in long index);
/**
* An ID to help identify this entry from others during
* subframe navigation
*/
attribute unsigned long ID;
/**
* Clear the child shell list.
*/
void clearChildShells();
/**
* pageIdentifier is an integer that should be the same for two entries
* attached to the same docshell only if the two entries are entries for the
* same page in the sense that one could go from the state represented by one
* to the state represented by the other simply by scrolling (so the entries
* are separated by an anchor traversal or a subframe navigation in some other
* frame).
*/
attribute unsigned long pageIdentifier;
/** Saved security state for the content viewer */
attribute nsISupports securityState;
/** attribute to set and get the cache key for the entry */
attribute nsISupports cacheKey;
/** Saved refresh URI list for the content viewer */
attribute nsISupportsArray refreshURIList;
/** attribute to indicate whether layoutHistoryState should be saved */
attribute boolean saveLayoutStateFlag;
/**
* Ensure that the cached presentation members are self-consistent.
* If either |contentViewer| or |windowState| are null, then all of the
* following members are cleared/reset:
* contentViewer, sticky, windowState, viewerBounds, childShells,
* refreshURIList.
*/
void syncPresentationState();
/** attribute to indicate whether the page is already expired in cache */
attribute boolean expirationStatus;
/** Title for the document */
void setTitle(in AString aTitle);
/** attribute to indicate the content-type of the document that this
is a session history entry for */
attribute ACString contentType;
/** Post Data for the document */
attribute nsIInputStream postData;
/** LayoutHistoryState for scroll position and form values */
attribute nsILayoutHistoryState layoutHistoryState;
/** parent of this entry */
attribute nsISHEntry parent;
/**
* The loadType for this entry. This is typically loadHistory except
* when reload is pressed, it has the appropriate reload flag
*/
attribute unsigned long loadType;
/**
* An ID to help identify this entry from others during
* subframe navigation
*/
attribute unsigned long ID;
/**
* pageIdentifier is an integer that should be the same for two entries
* attached to the same docshell only if the two entries are entries for
* the same page in the sense that one could go from the state represented
* by one to the state represented by the other simply by scrolling (so the
* entries are separated by an anchor traversal or a subframe navigation in
* some other frame).
*/
attribute unsigned long pageIdentifier;
/** attribute to set and get the cache key for the entry */
attribute nsISupports cacheKey;
/** attribute to indicate whether layoutHistoryState should be saved */
attribute boolean saveLayoutStateFlag;
/** attribute to indicate whether the page is already expired in cache */
attribute boolean expirationStatus;
/**
* attribute to indicate the content-type of the document that this
* is a session history entry for
*/
attribute ACString contentType;
/** Set/Get scrollers' positon in anchored pages */
void setScrollPosition(in PRInt32 x, in PRInt32 y);
void getScrollPosition(out PRInt32 x, out PRInt32 y);
/** Set/Get scrollers' positon in anchored pages */
void setScrollPosition(in long x, in long y);
void getScrollPosition(out long x, out long y);
/** Additional ways to create an entry */
void create(in nsIURI aURI, in wstring aTitle, in nsIDOMDocument aDocument,
in nsIInputStream aInputStream, in nsILayoutHistoryState aHistoryLayoutState,
in nsISupports aCacheKey, in ACString aContentType);
nsISHEntry clone();
/** Attribute that indicates if this entry is for a subframe navigation */
void SetIsSubFrame(in boolean aFlag);
/** Additional ways to create an entry */
void create(in nsIURI URI, in AString title,
in nsIInputStream inputStream,
in nsILayoutHistoryState layoutHistoryState,
in nsISupports cacheKey, in ACString contentType);
nsISHEntry clone();
/** Attribute that indicates if this entry is for a subframe navigation */
void setIsSubFrame(in boolean aFlag);
};

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

@ -46,7 +46,7 @@ interface nsIDocShell;
%{C++
#define NS_SHISTORY_INTERNAL_CID \
{0xdd335422, 0xb8b8, 0x11d3, {0xbd, 0xc8, 0x00, 0x50, 0x04, 0x0a, 0x9b, 0x44}}
{0x5b4cba4c, 0xbf67, 0x499a, {0xae, 0x2c, 0x3f, 0x76, 0x65, 0x6f, 0x4a, 0x4e}}
#define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
%}
@ -90,4 +90,11 @@ 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.
*/
void evictContentViewers();
};

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

@ -55,6 +55,7 @@ REQUIRES = xpcom \
layout \
docshell \
pref \
gfx \
$(NULL)
CPPSRCS = nsSHEntry.cpp \

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

@ -37,11 +37,16 @@
*
* ***** END LICENSE BLOCK ***** */
#ifdef DEBUG_bryner
#define DEBUG_PAGE_CACHE
#endif
// Local Includes
#include "nsSHEntry.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIDocShellTreeItem.h"
static PRUint32 gEntryID = 0;
@ -58,7 +63,9 @@ nsSHEntry::nsSHEntry()
, mIsFrameNavigation(PR_FALSE)
, mSaveLayoutState(PR_TRUE)
, mExpired(PR_FALSE)
, mSticky(PR_TRUE)
, mParent(nsnull)
, mViewerBounds(0, 0, 0, 0)
{
}
@ -77,12 +84,21 @@ nsSHEntry::nsSHEntry(const nsSHEntry &other)
, mIsFrameNavigation(other.mIsFrameNavigation)
, mSaveLayoutState(other.mSaveLayoutState)
, mExpired(other.mExpired)
, mSticky(PR_TRUE)
// XXX why not copy mContentType?
, mCacheKey(other.mCacheKey)
, mParent(other.mParent)
, mViewerBounds(0, 0, 0, 0)
{
}
nsSHEntry::~nsSHEntry()
{
mChildren.Clear();
if (mContentViewer)
mContentViewer->Destroy();
}
//*****************************************************************************
// nsSHEntry: nsISupports
//*****************************************************************************
@ -133,19 +149,35 @@ NS_IMETHODIMP nsSHEntry::SetReferrerURI(nsIURI *aReferrerURI)
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::SetDocument(nsIDOMDocument* aDocument)
NS_IMETHODIMP
nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
{
mDocument = aDocument;
mContentViewer = aViewer;
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::GetDocument(nsIDOMDocument** aResult)
NS_IMETHODIMP
nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
{
*aResult = mDocument;
*aResult = mContentViewer;
NS_IF_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetSticky(PRBool aSticky)
{
mSticky = aSticky;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetSticky(PRBool *aSticky)
{
*aSticky = mSticky;
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::GetTitle(PRUnichar** aTitle)
{
// Check for empty title...
@ -160,7 +192,7 @@ NS_IMETHODIMP nsSHEntry::GetTitle(PRUnichar** aTitle)
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::SetTitle(const PRUnichar* aTitle)
NS_IMETHODIMP nsSHEntry::SetTitle(const nsAString &aTitle)
{
mTitle = aTitle;
return NS_OK;
@ -297,14 +329,13 @@ NS_IMETHODIMP nsSHEntry::SetContentType(const nsACString& aContentType)
}
NS_IMETHODIMP
nsSHEntry::Create(nsIURI * aURI, const PRUnichar * aTitle,
nsIDOMDocument * aDOMDocument, nsIInputStream * aInputStream,
nsILayoutHistoryState * aHistoryLayoutState,
nsSHEntry::Create(nsIURI * aURI, const nsAString &aTitle,
nsIInputStream * aInputStream,
nsILayoutHistoryState * aLayoutHistoryState,
nsISupports * aCacheKey, const nsACString& aContentType)
{
mURI = aURI;
mTitle = aTitle;
mDocument = aDOMDocument;
mPostData = aInputStream;
mCacheKey = aCacheKey;
mContentType = aContentType;
@ -317,9 +348,9 @@ nsSHEntry::Create(nsIURI * aURI, const PRUnichar * aTitle,
// all subframe navigations, sets the flag to true.
mIsFrameNavigation = PR_FALSE;
// By default we save HistoryLayoutState
// By default we save LayoutHistoryState
mSaveLayoutState = PR_TRUE;
mLayoutHistoryState = aHistoryLayoutState;
mLayoutHistoryState = aLayoutHistoryState;
//By default the page is not expired
mExpired = PR_FALSE;
@ -358,6 +389,34 @@ nsSHEntry::SetParent(nsISHEntry * aParent)
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetWindowState(nsISupports *aState)
{
mWindowState = aState;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetWindowState(nsISupports **aState)
{
NS_IF_ADDREF(*aState = mWindowState);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetViewerBounds(const nsRect &aBounds)
{
mViewerBounds = aBounds;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetViewerBounds(nsRect &aBounds)
{
aBounds = mViewerBounds;
return NS_OK;
}
//*****************************************************************************
// nsSHEntry: nsISHContainer
//*****************************************************************************
@ -416,3 +475,77 @@ nsSHEntry::GetChildAt(PRInt32 aIndex, nsISHEntry ** aResult)
}
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::AddChildShell(nsIDocShellTreeItem *aShell)
{
NS_ASSERTION(aShell, "Null child shell added to history entry");
mChildShells.AppendElement(aShell);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::ChildShellAt(PRInt32 aIndex, nsIDocShellTreeItem **aShell)
{
NS_IF_ADDREF(*aShell =
NS_STATIC_CAST(nsIDocShellTreeItem*,
mChildShells.SafeElementAt(aIndex)));
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::ClearChildShells()
{
mChildShells.Clear();
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetSecurityState(nsISupports **aState)
{
NS_IF_ADDREF(*aState = mSecurityState);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetSecurityState(nsISupports *aState)
{
mSecurityState = aState;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::GetRefreshURIList(nsISupportsArray **aList)
{
NS_IF_ADDREF(*aList = mRefreshURIList);
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SetRefreshURIList(nsISupportsArray *aList)
{
mRefreshURIList = aList;
return NS_OK;
}
NS_IMETHODIMP
nsSHEntry::SyncPresentationState()
{
if (mContentViewer && mWindowState) {
// If we have a content viewer and a window state, we should be ok.
return NS_OK;
}
// If not, then nuke all of the presentation-related members.
if (mContentViewer)
mContentViewer->SetHistoryEntry(nsnull);
mContentViewer = nsnull;
mSticky = PR_TRUE;
mWindowState = nsnull;
mViewerBounds.SetRect(0, 0, 0, 0);
mChildShells.Clear();
mSecurityState = nsnull;
mRefreshURIList = nsnull;
return NS_OK;
}

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

@ -44,9 +44,10 @@
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsString.h"
#include "nsVoidArray.h"
// Interfaces needed
#include "nsIDOMDocument.h"
#include "nsIContentViewer.h"
#include "nsIInputStream.h"
#include "nsILayoutHistoryState.h"
#include "nsISHEntry.h"
@ -54,9 +55,10 @@
#include "nsIURI.h"
#include "nsIEnumerator.h"
#include "nsIHistoryEntry.h"
#include "nsRect.h"
#include "nsSupportsArray.h"
class nsSHEntry : public nsIHistoryEntry,
public nsISHEntry,
class nsSHEntry : public nsISHEntry,
public nsISHContainer
{
public:
@ -69,11 +71,11 @@ public:
NS_DECL_NSISHCONTAINER
private:
~nsSHEntry() { mChildren.Clear(); }
~nsSHEntry();
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIURI> mReferrerURI;
nsCOMPtr<nsIDOMDocument> mDocument;
nsCOMPtr<nsIContentViewer> mContentViewer;
nsString mTitle;
nsCOMPtr<nsIInputStream> mPostData;
nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
@ -86,9 +88,15 @@ private:
PRPackedBool mIsFrameNavigation;
PRPackedBool mSaveLayoutState;
PRPackedBool mExpired;
PRPackedBool mSticky;
nsCString mContentType;
nsCOMPtr<nsISupports> mCacheKey;
nsISHEntry * mParent; // weak reference
nsCOMPtr<nsISupports> mWindowState;
nsRect mViewerBounds;
nsVoidArray mChildShells;
nsCOMPtr<nsISupports> mSecurityState;
nsCOMPtr<nsISupportsArray> mRefreshURIList;
};
#endif /* nsSHEntry_h */

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

@ -54,9 +54,14 @@
#include "nsIDocShellLoadInfo.h"
#include "nsIServiceManager.h"
#include "nsIPrefService.h"
#include "nsIURI.h"
#include "nsIContentViewer.h"
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
#define PREF_SHISTORY_VIEWERS "browser.sessionhistory.max_viewers"
static PRInt32 gHistoryMaxSize = 50;
static PRInt32 gHistoryMaxViewers = 0;
enum HistCmd{
HIST_CMD_BACK,
@ -104,11 +109,21 @@ nsSHistory::Init()
{
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs) {
// Session history size is only taken from the default prefs branch.
// This means that it's only configurable on a per-application basis.
// The goal of this is to unbreak users who have inadvertently set their
// session history size to -1.
nsCOMPtr<nsIPrefBranch> defaultBranch;
prefs->GetDefaultBranch(nsnull, getter_AddRefs(defaultBranch));
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);
}
return NS_OK;
}
@ -207,6 +222,7 @@ 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
@ -476,6 +492,14 @@ nsSHistory::GetListener(nsISHistoryListener ** aListener)
return NS_OK;
}
NS_IMETHODIMP
nsSHistory::EvictContentViewers()
{
// This is called after a new entry has been appended to the end of the list.
EvictContentViewers(mIndex - 1, mIndex);
return NS_OK;
}
//*****************************************************************************
// nsSHistory: nsIWebNavigation
//*****************************************************************************
@ -583,12 +607,63 @@ nsSHistory::Reload(PRUint32 aReloadFlags)
return LoadEntry(mIndex, loadType, HIST_CMD_RELOAD);
}
void
nsSHistory::EvictContentViewers(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.
PRInt32 startIndex, endIndex;
if (aToIndex > aFromIndex) { // going forward
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);
}
nsCOMPtr<nsISHTransaction> trans;
GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
for (PRInt32 i = startIndex; trans && i < endIndex; ++i) {
nsCOMPtr<nsISHEntry> entry;
trans->GetSHEntry(getter_AddRefs(entry));
nsCOMPtr<nsIContentViewer> viewer;
entry->GetContentViewer(getter_AddRefs(viewer));
if (viewer) {
#ifdef DEBUG_PAGE_CACHE
nsCOMPtr<nsIURI> uri;
entry->GetURI(getter_AddRefs(uri));
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
printf("Evicting content viewer: %s\n", spec.get());
#endif
viewer->Destroy();
entry->SetContentViewer(nsnull);
entry->SyncPresentationState();
}
nsISHTransaction *temp = trans;
temp->GetNext(getter_AddRefs(trans));
}
}
NS_IMETHODIMP
nsSHistory::UpdateIndex()
{
// Update the actual index with the right value.
if (mIndex != mRequestedIndex && mRequestedIndex != -1)
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;
}
return NS_OK;
}

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

@ -85,6 +85,8 @@ protected:
nsresult PrintHistory();
#endif
void EvictContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
protected:
nsCOMPtr<nsISHTransaction> mListRoot;
PRInt32 mIndex;