diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 989ff3a09d6..c49ea13dcc2 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -2166,6 +2166,37 @@ nsDocShell::HistoryPurged(PRInt32 aNumEntries) return NS_OK; } +nsresult +nsDocShell::HistoryTransactionRemoved(PRInt32 aIndex) +{ + // These indices are used for fastback cache eviction, to determine + // which session history entries are candidates for content viewer + // eviction. We need to adjust by the number of entries that we + // just purged from history, so that we look at the right session history + // entries during eviction. + if (aIndex == mPreviousTransIndex) { + mPreviousTransIndex = -1; + } else if (aIndex < mPreviousTransIndex) { + --mPreviousTransIndex; + } + if (mLoadedTransIndex == aIndex) { + mLoadedTransIndex = 0; + } else if (aIndex < mLoadedTransIndex) { + --mLoadedTransIndex; + } + + PRInt32 count = mChildList.Count(); + for (PRInt32 i = 0; i < count; ++i) { + nsCOMPtr shell = do_QueryInterface(ChildAt(i)); + if (shell) { + static_cast(shell.get())-> + HistoryTransactionRemoved(aIndex); + } + } + + return NS_OK; +} + static nsresult GetPrincipalDomain(nsIPrincipal* aPrincipal, nsACString& aDomain) @@ -9628,7 +9659,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle, AddURIVisit(newURI, oldURI, oldURI, 0); } else { - FireOnLocationChange(this, nsnull, mCurrentURI); + FireDummyOnLocationChange(); } return NS_OK; diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index b0010a3f8ee..2e95717263a 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -269,6 +269,13 @@ public: friend class OnLinkClickEvent; + // We need dummy OnLocationChange in some cases to update the UI. + void FireDummyOnLocationChange() + { + FireOnLocationChange(this, nsnull, mCurrentURI); + } + + nsresult HistoryTransactionRemoved(PRInt32 aIndex); protected: // Object Management virtual ~nsDocShell(); diff --git a/docshell/shistory/src/nsSHTransaction.cpp b/docshell/shistory/src/nsSHTransaction.cpp index 7df15527a41..2050349a910 100644 --- a/docshell/shistory/src/nsSHTransaction.cpp +++ b/docshell/shistory/src/nsSHTransaction.cpp @@ -111,10 +111,12 @@ nsSHTransaction::GetNext(nsISHTransaction * * aResult) NS_IMETHODIMP nsSHTransaction::SetNext(nsISHTransaction * aNext) { - NS_ENSURE_SUCCESS(aNext->SetPrev(this), NS_ERROR_FAILURE); + if (aNext) { + NS_ENSURE_SUCCESS(aNext->SetPrev(this), NS_ERROR_FAILURE); + } - mNext = aNext; - return NS_OK; + mNext = aNext; + return NS_OK; } NS_IMETHODIMP diff --git a/docshell/shistory/src/nsSHistory.cpp b/docshell/shistory/src/nsSHistory.cpp index d17ee517377..d20523e6e0e 100644 --- a/docshell/shistory/src/nsSHistory.cpp +++ b/docshell/shistory/src/nsSHistory.cpp @@ -62,6 +62,7 @@ #include "mozilla/Services.h" #include "nsTArray.h" #include "nsCOMArray.h" +#include "nsDocShell.h" // For calculating max history entries and max cachable contentviewers #include "nspr.h" @@ -1137,13 +1138,91 @@ PRBool RemoveChildEntries(nsISHistory* aHistory, PRInt32 aIndex, return root ? RemoveFromSessionHistoryContainer(root, aEntryIDs) : PR_FALSE; } +PRBool IsSameTree(nsISHEntry* aEntry1, nsISHEntry* aEntry2) +{ + if (!aEntry1 && !aEntry2) { + return PR_TRUE; + } + if ((!aEntry1 && aEntry2) || (aEntry1 && !aEntry2)) { + return PR_FALSE; + } + PRUint32 id1, id2; + aEntry1->GetID(&id1); + aEntry2->GetID(&id2); + if (id1 != id2) { + return PR_FALSE; + } + + nsCOMPtr container1 = do_QueryInterface(aEntry1); + nsCOMPtr container2 = do_QueryInterface(aEntry2); + PRInt32 count1, count2; + container1->GetChildCount(&count1); + container2->GetChildCount(&count2); + // We allow null entries in the end of the child list. + PRInt32 count = PR_MAX(count1, count2); + for (PRInt32 i = 0; i < count; ++i) { + nsCOMPtr child1, child2; + container1->GetChildAt(i, getter_AddRefs(child1)); + container2->GetChildAt(i, getter_AddRefs(child2)); + if (!IsSameTree(child1, child2)) { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +PRBool +nsSHistory::RemoveDuplicate(PRInt32 aIndex) +{ + NS_ASSERTION(aIndex > 0, "aIndex must be > 0!"); + nsCOMPtr rootHE1, rootHE2; + GetEntryAtIndex(aIndex, PR_FALSE, getter_AddRefs(rootHE1)); + GetEntryAtIndex(aIndex - 1, PR_FALSE, getter_AddRefs(rootHE2)); + nsCOMPtr root1 = do_QueryInterface(rootHE1); + nsCOMPtr root2 = do_QueryInterface(rootHE2); + if (IsSameTree(root1, root2)) { + nsCOMPtr txToRemove, txToKeep, txNext; + GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove)); + GetTransactionAtIndex(aIndex - 1, getter_AddRefs(txToKeep)); + NS_ENSURE_TRUE(txToRemove, PR_FALSE); + NS_ENSURE_TRUE(txToKeep, PR_FALSE); + txToRemove->GetNext(getter_AddRefs(txNext)); + txToRemove->SetNext(nsnull); + txToRemove->SetPrev(nsnull); + // If txNext is non-null, this will set txNext's .prev + txToKeep->SetNext(txNext); + static_cast(mRootDocShell)->HistoryTransactionRemoved(aIndex); + if (mIndex >= aIndex) { + mIndex = mIndex - 1; + } + --mLength; + return PR_TRUE; + } + return PR_FALSE; +} + NS_IMETHODIMP_(void) nsSHistory::RemoveEntries(nsTArray& aIDs, PRInt32 aStartIndex) { PRInt32 index = aStartIndex; while(index >= 0 && RemoveChildEntries(this, --index, aIDs)); + // Nothing was removed from minIndex if it is > 0! + PRInt32 minIndex = index >= 0 ? index : 0; index = aStartIndex; while(index >= 0 && RemoveChildEntries(this, index++, aIDs)); + + // We need to remove duplicate nsSHEntry trees. + PRBool didRemove = PR_FALSE; + while (index && index > minIndex) { + didRemove = RemoveDuplicate(index--) || didRemove; + } + if (didRemove && mRootDocShell) { + nsRefPtr ev = + NS_NewRunnableMethod(static_cast(mRootDocShell), + &nsDocShell::FireDummyOnLocationChange); + NS_DispatchToCurrentThread(ev); + } } void diff --git a/docshell/shistory/src/nsSHistory.h b/docshell/shistory/src/nsSHistory.h index 25fd56424c3..9eb52a7a2e3 100644 --- a/docshell/shistory/src/nsSHistory.h +++ b/docshell/shistory/src/nsSHistory.h @@ -116,6 +116,9 @@ protected: nsresult LoadNextPossibleEntry(PRInt32 aNewIndex, long aLoadType, PRUint32 aHistCmd); protected: + // Note, aIndex must be > 0, since it is compared to aIndex - 1. + PRBool RemoveDuplicate(PRInt32 aIndex); + nsCOMPtr mListRoot; PRInt32 mIndex; PRInt32 mLength;