diff --git a/docshell/shistory/public/nsIHistoryEntry.idl b/docshell/shistory/public/nsIHistoryEntry.idl index cb2ff7b9273..41f6c5e0a23 100644 --- a/docshell/shistory/public/nsIHistoryEntry.idl +++ b/docshell/shistory/public/nsIHistoryEntry.idl @@ -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; }; diff --git a/docshell/shistory/public/nsISHEntry.idl b/docshell/shistory/public/nsISHEntry.idl index 9e5d7336489..f3836dbcba5 100644 --- a/docshell/shistory/public/nsISHEntry.idl +++ b/docshell/shistory/public/nsISHEntry.idl @@ -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); }; diff --git a/docshell/shistory/public/nsISHistoryInternal.idl b/docshell/shistory/public/nsISHistoryInternal.idl index 318c8e33bc2..d4f5272611e 100644 --- a/docshell/shistory/public/nsISHistoryInternal.idl +++ b/docshell/shistory/public/nsISHistoryInternal.idl @@ -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(); }; diff --git a/docshell/shistory/src/Makefile.in b/docshell/shistory/src/Makefile.in index 6db8eeb7ad8..907d812d51b 100644 --- a/docshell/shistory/src/Makefile.in +++ b/docshell/shistory/src/Makefile.in @@ -55,6 +55,7 @@ REQUIRES = xpcom \ layout \ docshell \ pref \ + gfx \ $(NULL) CPPSRCS = nsSHEntry.cpp \ diff --git a/docshell/shistory/src/nsSHEntry.cpp b/docshell/shistory/src/nsSHEntry.cpp index 313e507b16b..03a2b47ad5f 100644 --- a/docshell/shistory/src/nsSHEntry.cpp +++ b/docshell/shistory/src/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; +} diff --git a/docshell/shistory/src/nsSHEntry.h b/docshell/shistory/src/nsSHEntry.h index f4fa459a004..db207f7e99a 100644 --- a/docshell/shistory/src/nsSHEntry.h +++ b/docshell/shistory/src/nsSHEntry.h @@ -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 mURI; nsCOMPtr mReferrerURI; - nsCOMPtr mDocument; + nsCOMPtr mContentViewer; nsString mTitle; nsCOMPtr mPostData; nsCOMPtr mLayoutHistoryState; @@ -86,9 +88,15 @@ private: PRPackedBool mIsFrameNavigation; PRPackedBool mSaveLayoutState; PRPackedBool mExpired; + PRPackedBool mSticky; nsCString mContentType; nsCOMPtr mCacheKey; nsISHEntry * mParent; // weak reference + nsCOMPtr mWindowState; + nsRect mViewerBounds; + nsVoidArray mChildShells; + nsCOMPtr mSecurityState; + nsCOMPtr mRefreshURIList; }; #endif /* nsSHEntry_h */ diff --git a/docshell/shistory/src/nsSHistory.cpp b/docshell/shistory/src/nsSHistory.cpp index 0334d2bc910..da2cebb2cfc 100644 --- a/docshell/shistory/src/nsSHistory.cpp +++ b/docshell/shistory/src/nsSHistory.cpp @@ -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 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 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 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 trans; + GetTransactionAtIndex(startIndex, getter_AddRefs(trans)); + + for (PRInt32 i = startIndex; trans && i < endIndex; ++i) { + nsCOMPtr entry; + trans->GetSHEntry(getter_AddRefs(entry)); + nsCOMPtr viewer; + entry->GetContentViewer(getter_AddRefs(viewer)); + if (viewer) { +#ifdef DEBUG_PAGE_CACHE + nsCOMPtr 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; } diff --git a/docshell/shistory/src/nsSHistory.h b/docshell/shistory/src/nsSHistory.h index d96cc9d5d34..2d21404a978 100644 --- a/docshell/shistory/src/nsSHistory.h +++ b/docshell/shistory/src/nsSHistory.h @@ -85,6 +85,8 @@ protected: nsresult PrintHistory(); #endif + void EvictContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex); + protected: nsCOMPtr mListRoot; PRInt32 mIndex;