--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -277,18 +277,16 @@ FavorPerformanceHint(bool aPerfOverStarv
if (appShell) {
appShell->FavorPerformanceHint(
aPerfOverStarvation,
Preferences::GetUint("docshell.event_starvation_delay_hint",
NS_EVENT_STARVATION_DELAY_HINT));
}
}
-static nsISHEntry* GetRootSHEntry(nsISHEntry* aEntry);
-
static void
IncreasePrivateDocShellCount()
{
gNumberOfPrivateDocShells++;
if (gNumberOfPrivateDocShells > 1 ||
!XRE_IsContentProcess()) {
return;
}
@@ -3972,18 +3970,18 @@ nsDocShell::AddChildSHEntryInternal(nsIS
getter_AddRefs(currentHE));
NS_ENSURE_TRUE(currentHE, NS_ERROR_FAILURE);
nsCOMPtr<nsISHEntry> currentEntry(do_QueryInterface(currentHE));
if (currentEntry) {
uint32_t cloneID = 0;
nsCOMPtr<nsISHEntry> nextEntry;
aCloneRef->GetID(&cloneID);
- rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
- aCloneChildren, getter_AddRefs(nextEntry));
+ rv = nsSHistory::CloneAndReplace(currentEntry, this, cloneID,
+ aNewEntry, aCloneChildren, getter_AddRefs(nextEntry));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsISHistoryInternal> shPrivate =
do_QueryInterface(mSessionHistory);
NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
rv = shPrivate->AddEntry(nextEntry, true);
}
}
@@ -11961,17 +11959,17 @@ nsDocShell::AddState(JS::Handle<JS::Valu
if (!aReplace) {
int32_t curIndex = -1;
rv = rootSH->GetIndex(&curIndex);
if (NS_SUCCEEDED(rv) && curIndex > -1) {
internalSH->EvictOutOfRangeContentViewers(curIndex);
}
} else {
- nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
+ nsCOMPtr<nsISHEntry> rootSHEntry = nsSHistory::GetRootSHEntry(newSHEntry);
int32_t index = -1;
rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
if (NS_SUCCEEDED(rv) && index > -1) {
internalSH->ReplaceEntry(index, rootSHEntry);
}
}
@@ -12261,18 +12259,18 @@ nsDocShell::AddToSessionHistory(nsIURI*
if (root == static_cast<nsIDocShellTreeItem*>(this) && mSessionHistory) {
// If we need to clone our children onto the new session
// history entry, do so now.
if (aCloneChildren && mOSHE) {
uint32_t cloneID;
mOSHE->GetID(&cloneID);
nsCOMPtr<nsISHEntry> newEntry;
- CloneAndReplace(mOSHE, this, cloneID, entry, true,
- getter_AddRefs(newEntry));
+ nsSHistory::CloneAndReplace(mOSHE, this, cloneID, entry, true,
+ getter_AddRefs(newEntry));
NS_ASSERTION(entry == newEntry,
"The new session history should be in the new entry");
}
// This is the root docshell
bool addToSHistory = !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY);
if (!addToSHistory) {
// Replace current entry in session history; If the requested index is
@@ -12507,275 +12505,59 @@ nsDocShell::PersistLayoutHistoryState()
if (scrollRestorationIsManual && layoutState) {
layoutState->ResetScrollState();
}
}
return rv;
}
-/* static */ nsresult
-nsDocShell::WalkHistoryEntries(nsISHEntry* aRootEntry,
- nsDocShell* aRootShell,
- WalkHistoryEntriesFunc aCallback,
- void* aData)
-{
- NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
-
- nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
- if (!container) {
- return NS_ERROR_FAILURE;
- }
-
- int32_t childCount;
- container->GetChildCount(&childCount);
- for (int32_t i = 0; i < childCount; i++) {
- nsCOMPtr<nsISHEntry> childEntry;
- container->GetChildAt(i, getter_AddRefs(childEntry));
- if (!childEntry) {
- // childEntry can be null for valid reasons, for example if the
- // docshell at index i never loaded anything useful.
- // Remember to clone also nulls in the child array (bug 464064).
- aCallback(nullptr, nullptr, i, aData);
- continue;
- }
-
- nsDocShell* childShell = nullptr;
- if (aRootShell) {
- // Walk the children of aRootShell and see if one of them
- // has srcChild as a SHEntry.
- nsTObserverArray<nsDocLoader*>::ForwardIterator iter(
- aRootShell->mChildList);
- while (iter.HasMore()) {
- nsDocShell* child = static_cast<nsDocShell*>(iter.GetNext());
-
- if (child->HasHistoryEntry(childEntry)) {
- childShell = child;
- break;
- }
- }
- }
- nsresult rv = aCallback(childEntry, childShell, i, aData);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- return NS_OK;
-}
-
-// callback data for WalkHistoryEntries
-struct MOZ_STACK_CLASS CloneAndReplaceData
-{
- CloneAndReplaceData(uint32_t aCloneID, nsISHEntry* aReplaceEntry,
- bool aCloneChildren, nsISHEntry* aDestTreeParent)
- : cloneID(aCloneID)
- , cloneChildren(aCloneChildren)
- , replaceEntry(aReplaceEntry)
- , destTreeParent(aDestTreeParent)
- {
- }
-
- uint32_t cloneID;
- bool cloneChildren;
- nsISHEntry* replaceEntry;
- nsISHEntry* destTreeParent;
- nsCOMPtr<nsISHEntry> resultEntry;
-};
-
-/* static */ nsresult
-nsDocShell::CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
- int32_t aEntryIndex, void* aData)
-{
- nsCOMPtr<nsISHEntry> dest;
-
- CloneAndReplaceData* data = static_cast<CloneAndReplaceData*>(aData);
- uint32_t cloneID = data->cloneID;
- nsISHEntry* replaceEntry = data->replaceEntry;
-
- nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
- if (!aEntry) {
- if (container) {
- container->AddChild(nullptr, aEntryIndex);
- }
- return NS_OK;
- }
-
- uint32_t srcID;
- aEntry->GetID(&srcID);
-
- nsresult rv = NS_OK;
- if (srcID == cloneID) {
- // Replace the entry
- dest = replaceEntry;
- } else {
- // Clone the SHEntry...
- rv = aEntry->Clone(getter_AddRefs(dest));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- dest->SetIsSubFrame(true);
-
- if (srcID != cloneID || data->cloneChildren) {
- // Walk the children
- CloneAndReplaceData childData(cloneID, replaceEntry,
- data->cloneChildren, dest);
- rv = WalkHistoryEntries(aEntry, aShell,
- CloneAndReplaceChild, &childData);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- if (srcID != cloneID && aShell) {
- aShell->SwapHistoryEntries(aEntry, dest);
- }
-
- if (container) {
- container->AddChild(dest, aEntryIndex);
- }
-
- data->resultEntry = dest;
- return rv;
-}
-
-/* static */ nsresult
-nsDocShell::CloneAndReplace(nsISHEntry* aSrcEntry,
- nsDocShell* aSrcShell,
- uint32_t aCloneID,
- nsISHEntry* aReplaceEntry,
- bool aCloneChildren,
- nsISHEntry** aResultEntry)
-{
- NS_ENSURE_ARG_POINTER(aResultEntry);
- NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
-
- CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
- nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
-
- data.resultEntry.swap(*aResultEntry);
- return rv;
-}
-
void
nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry)
{
if (aOldEntry == mOSHE) {
mOSHE = aNewEntry;
}
if (aOldEntry == mLSHE) {
mLSHE = aNewEntry;
}
}
-struct SwapEntriesData
-{
- nsDocShell* ignoreShell; // constant; the shell to ignore
- nsISHEntry* destTreeRoot; // constant; the root of the dest tree
- nsISHEntry* destTreeParent; // constant; the node under destTreeRoot
- // whose children will correspond to aEntry
-};
-
-nsresult
-nsDocShell::SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
- int32_t aEntryIndex, void* aData)
-{
- SwapEntriesData* data = static_cast<SwapEntriesData*>(aData);
- nsDocShell* ignoreShell = data->ignoreShell;
-
- if (!aShell || aShell == ignoreShell) {
- return NS_OK;
- }
-
- nsISHEntry* destTreeRoot = data->destTreeRoot;
-
- nsCOMPtr<nsISHEntry> destEntry;
- nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
-
- if (container) {
- // aEntry is a clone of some child of destTreeParent, but since the
- // trees aren't necessarily in sync, we'll have to locate it.
- // Note that we could set aShell's entry to null if we don't find a
- // corresponding entry under destTreeParent.
-
- uint32_t targetID, id;
- aEntry->GetID(&targetID);
-
- // First look at the given index, since this is the common case.
- nsCOMPtr<nsISHEntry> entry;
- container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
- if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
- destEntry.swap(entry);
- } else {
- int32_t childCount;
- container->GetChildCount(&childCount);
- for (int32_t i = 0; i < childCount; ++i) {
- container->GetChildAt(i, getter_AddRefs(entry));
- if (!entry) {
- continue;
- }
-
- entry->GetID(&id);
- if (id == targetID) {
- destEntry.swap(entry);
- break;
- }
- }
- }
- } else {
- destEntry = destTreeRoot;
- }
-
- aShell->SwapHistoryEntries(aEntry, destEntry);
-
- // Now handle the children of aEntry.
- SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
- return WalkHistoryEntries(aEntry, aShell, SetChildHistoryEntry, &childData);
-}
-
-static nsISHEntry*
-GetRootSHEntry(nsISHEntry* aEntry)
-{
- nsCOMPtr<nsISHEntry> rootEntry = aEntry;
- nsISHEntry* result = nullptr;
- while (rootEntry) {
- result = rootEntry;
- result->GetParent(getter_AddRefs(rootEntry));
- }
-
- return result;
-}
-
void
nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry)
{
// We need to sync up the docshell and session history trees for
// subframe navigation. If the load was in a subframe, we forward up to
// the root docshell, which will then recursively sync up all docshells
// to their corresponding entries in the new session history tree.
// If we don't do this, then we can cache a content viewer on the wrong
// cloned entry, and subsequently restore it at the wrong time.
- nsISHEntry* newRootEntry = GetRootSHEntry(aEntry);
+ nsISHEntry* newRootEntry = nsSHistory::GetRootSHEntry(aEntry);
if (newRootEntry) {
// newRootEntry is now the new root entry.
// Find the old root entry as well.
// Need a strong ref. on |oldRootEntry| so it isn't destroyed when
// SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
- nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
+ nsCOMPtr<nsISHEntry> oldRootEntry = nsSHistory::GetRootSHEntry(*aPtr);
if (oldRootEntry) {
nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
if (rootShell) { // if we're the root just set it, nothing to swap
- SwapEntriesData data = { this, newRootEntry };
+ nsSHistory::SwapEntriesData data = { this, newRootEntry };
nsIDocShell* rootIDocShell = static_cast<nsIDocShell*>(rootShell);
nsDocShell* rootDocShell = static_cast<nsDocShell*>(rootIDocShell);
#ifdef DEBUG
nsresult rv =
#endif
- SetChildHistoryEntry(oldRootEntry, rootDocShell, 0, &data);
+ nsSHistory::SetChildHistoryEntry(oldRootEntry, rootDocShell, 0, &data);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetChildHistoryEntry failed");
}
}
}
*aPtr = aEntry;
}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -352,16 +352,26 @@ public:
mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
}
const mozilla::OriginAttributes& GetOriginAttributes()
{
return mOriginAttributes;
}
+ // Determine whether this docshell corresponds to the given history entry,
+ // via having a pointer to it in mOSHE or mLSHE.
+ bool HasHistoryEntry(nsISHEntry* aEntry) const
+ {
+ return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
+ }
+
+ // Update any pointers (mOSHE or mLSHE) to aOldEntry to point to aNewEntry
+ void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry);
+
static bool SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags);
// Tell the favicon service that aNewURI has the same favicon as aOldURI.
static void CopyFavicon(nsIURI* aOldURI,
nsIURI* aNewURI,
nsIPrincipal* aLoadingPrincipal,
bool aInPrivateBrowsing);
@@ -387,57 +397,16 @@ private: // member functions
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, const char*, const TimeStamp&, MarkerTracingType,
MarkerStackRequest);
friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
friend void mozilla::TimelineConsumers::PopMarkers(nsDocShell*,
JSContext*, nsTArray<dom::ProfileTimelineMarker>&);
- // Callback prototype for WalkHistoryEntries.
- // aEntry is the child history entry, aShell is its corresponding docshell,
- // aChildIndex is the child's index in its parent entry, and aData is
- // the opaque pointer passed to WalkHistoryEntries.
- typedef nsresult(*WalkHistoryEntriesFunc)(nsISHEntry* aEntry,
- nsDocShell* aShell,
- int32_t aChildIndex,
- void* aData);
-
- // Clone a session history tree for subframe navigation.
- // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
- // for the entry with id |aCloneID|, which will be replaced with
- // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
- // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
- // have that pointer updated to point to the cloned history entry.
- // If aCloneChildren is true then the children of the entry with id
- // |aCloneID| will be cloned into |aReplaceEntry|.
- static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
- nsDocShell* aSrcShell,
- uint32_t aCloneID,
- nsISHEntry* aReplaceEntry,
- bool aCloneChildren,
- nsISHEntry** aDestEntry);
-
- // Child-walking callback for CloneAndReplace
- static nsresult CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
- int32_t aChildIndex, void* aData);
-
-
- // Child-walking callback for SetHistoryEntry
- static nsresult SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
- int32_t aEntryIndex, void* aData);
-
- // For each child of aRootEntry, find the corresponding docshell which is
- // a child of aRootShell, and call aCallback. The opaque pointer aData
- // is passed to the callback.
- static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
- nsDocShell* aRootShell,
- WalkHistoryEntriesFunc aCallback,
- void* aData);
-
// Security checks to prevent frameset spoofing. See comments at
// implementation sites.
static bool CanAccessItem(nsIDocShellTreeItem* aTargetItem,
nsIDocShellTreeItem* aAccessingItem,
bool aConsiderOpener = true);
static bool ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
nsIDocShellTreeItem* aTargetTreeItem);
@@ -523,26 +492,16 @@ private: // member functions
nsresult AddChildSHEntryToParent(nsISHEntry* aNewEntry, int32_t aChildOffset,
bool aCloneChildren);
nsresult AddChildSHEntryInternal(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
int32_t aChildOffset, uint32_t aLoadType,
bool aCloneChildren);
- // Determine whether this docshell corresponds to the given history entry,
- // via having a pointer to it in mOSHE or mLSHE.
- bool HasHistoryEntry(nsISHEntry* aEntry) const
- {
- return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
- }
-
- // Update any pointers (mOSHE or mLSHE) to aOldEntry to point to aNewEntry
- void SwapHistoryEntries(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry);
-
// Call this method to swap in a new history entry to m[OL]SHE, rather than
// setting it directly. This completes the navigation in all docshells
// in the case of a subframe navigation.
void SetHistoryEntry(nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry);
//
// URI Load
//
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -372,16 +372,233 @@ nsSHistory::Shutdown()
if (obsSvc) {
obsSvc->RemoveObserver(gObserver, "cacheservice:empty-cache");
obsSvc->RemoveObserver(gObserver, "memory-pressure");
}
gObserver = nullptr;
}
}
+// static
+nsISHEntry*
+nsSHistory::GetRootSHEntry(nsISHEntry* aEntry)
+{
+ nsCOMPtr<nsISHEntry> rootEntry = aEntry;
+ nsISHEntry* result = nullptr;
+ while (rootEntry) {
+ result = rootEntry;
+ result->GetParent(getter_AddRefs(rootEntry));
+ }
+
+ return result;
+}
+
+// static
+nsresult
+nsSHistory::WalkHistoryEntries(nsISHEntry* aRootEntry,
+ nsDocShell* aRootShell,
+ WalkHistoryEntriesFunc aCallback,
+ void* aData)
+{
+ NS_ENSURE_TRUE(aRootEntry, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISHContainer> container(do_QueryInterface(aRootEntry));
+ if (!container) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; i++) {
+ nsCOMPtr<nsISHEntry> childEntry;
+ container->GetChildAt(i, getter_AddRefs(childEntry));
+ if (!childEntry) {
+ // childEntry can be null for valid reasons, for example if the
+ // docshell at index i never loaded anything useful.
+ // Remember to clone also nulls in the child array (bug 464064).
+ aCallback(nullptr, nullptr, i, aData);
+ continue;
+ }
+
+ nsDocShell* childShell = nullptr;
+ if (aRootShell) {
+ // Walk the children of aRootShell and see if one of them
+ // has srcChild as a SHEntry.
+ int32_t length;
+ aRootShell->GetChildCount(&length);
+ for (int32_t i = 0; i < length; i++) {
+ nsCOMPtr<nsIDocShellTreeItem> item;
+ nsresult rv = aRootShell->GetChildAt(i, getter_AddRefs(item));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsDocShell* child = static_cast<nsDocShell*>(item.get());
+ if (child->HasHistoryEntry(childEntry)) {
+ childShell = child;
+ break;
+ }
+ }
+ }
+ nsresult rv = aCallback(childEntry, childShell, i, aData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+// callback data for WalkHistoryEntries
+struct MOZ_STACK_CLASS CloneAndReplaceData
+{
+ CloneAndReplaceData(uint32_t aCloneID, nsISHEntry* aReplaceEntry,
+ bool aCloneChildren, nsISHEntry* aDestTreeParent)
+ : cloneID(aCloneID)
+ , cloneChildren(aCloneChildren)
+ , replaceEntry(aReplaceEntry)
+ , destTreeParent(aDestTreeParent)
+ {
+ }
+
+ uint32_t cloneID;
+ bool cloneChildren;
+ nsISHEntry* replaceEntry;
+ nsISHEntry* destTreeParent;
+ nsCOMPtr<nsISHEntry> resultEntry;
+};
+
+// static
+nsresult
+nsSHistory::CloneAndReplaceChild(nsISHEntry* aEntry,
+ nsDocShell* aShell,
+ int32_t aEntryIndex,
+ void* aData)
+{
+ nsCOMPtr<nsISHEntry> dest;
+
+ CloneAndReplaceData* data = static_cast<CloneAndReplaceData*>(aData);
+ uint32_t cloneID = data->cloneID;
+ nsISHEntry* replaceEntry = data->replaceEntry;
+
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+ if (!aEntry) {
+ if (container) {
+ container->AddChild(nullptr, aEntryIndex);
+ }
+ return NS_OK;
+ }
+
+ uint32_t srcID;
+ aEntry->GetID(&srcID);
+
+ nsresult rv = NS_OK;
+ if (srcID == cloneID) {
+ // Replace the entry
+ dest = replaceEntry;
+ } else {
+ // Clone the SHEntry...
+ rv = aEntry->Clone(getter_AddRefs(dest));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ dest->SetIsSubFrame(true);
+
+ if (srcID != cloneID || data->cloneChildren) {
+ // Walk the children
+ CloneAndReplaceData childData(cloneID, replaceEntry,
+ data->cloneChildren, dest);
+ rv = WalkHistoryEntries(aEntry, aShell,
+ CloneAndReplaceChild, &childData);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (srcID != cloneID && aShell) {
+ aShell->SwapHistoryEntries(aEntry, dest);
+ }
+
+ if (container) {
+ container->AddChild(dest, aEntryIndex);
+ }
+
+ data->resultEntry = dest;
+ return rv;
+}
+
+// static
+nsresult
+nsSHistory::CloneAndReplace(nsISHEntry* aSrcEntry,
+ nsDocShell* aSrcShell,
+ uint32_t aCloneID,
+ nsISHEntry* aReplaceEntry,
+ bool aCloneChildren,
+ nsISHEntry** aResultEntry)
+{
+ NS_ENSURE_ARG_POINTER(aResultEntry);
+ NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
+
+ CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nullptr);
+ nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
+
+ data.resultEntry.swap(*aResultEntry);
+ return rv;
+}
+
+// static
+nsresult
+nsSHistory::SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData)
+{
+ SwapEntriesData* data = static_cast<SwapEntriesData*>(aData);
+ nsDocShell* ignoreShell = data->ignoreShell;
+
+ if (!aShell || aShell == ignoreShell) {
+ return NS_OK;
+ }
+
+ nsISHEntry* destTreeRoot = data->destTreeRoot;
+
+ nsCOMPtr<nsISHEntry> destEntry;
+ nsCOMPtr<nsISHContainer> container = do_QueryInterface(data->destTreeParent);
+
+ if (container) {
+ // aEntry is a clone of some child of destTreeParent, but since the
+ // trees aren't necessarily in sync, we'll have to locate it.
+ // Note that we could set aShell's entry to null if we don't find a
+ // corresponding entry under destTreeParent.
+
+ uint32_t targetID, id;
+ aEntry->GetID(&targetID);
+
+ // First look at the given index, since this is the common case.
+ nsCOMPtr<nsISHEntry> entry;
+ container->GetChildAt(aEntryIndex, getter_AddRefs(entry));
+ if (entry && NS_SUCCEEDED(entry->GetID(&id)) && id == targetID) {
+ destEntry.swap(entry);
+ } else {
+ int32_t childCount;
+ container->GetChildCount(&childCount);
+ for (int32_t i = 0; i < childCount; ++i) {
+ container->GetChildAt(i, getter_AddRefs(entry));
+ if (!entry) {
+ continue;
+ }
+
+ entry->GetID(&id);
+ if (id == targetID) {
+ destEntry.swap(entry);
+ break;
+ }
+ }
+ }
+ } else {
+ destEntry = destTreeRoot;
+ }
+
+ aShell->SwapHistoryEntries(aEntry, destEntry);
+
+ // Now handle the children of aEntry.
+ SwapEntriesData childData = { ignoreShell, destTreeRoot, destEntry };
+ return WalkHistoryEntries(aEntry, aShell, SetChildHistoryEntry, &childData);
+}
+
/* Add an entry to the History list at mIndex and
* increment the index to point to the new entry
*/
NS_IMETHODIMP
nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist)
{
NS_ENSURE_ARG(aSHEntry);
--- a/docshell/shistory/nsSHistory.h
+++ b/docshell/shistory/nsSHistory.h
@@ -56,16 +56,25 @@ public:
}
private:
// HistoryTracker is owned by nsSHistory; it always outlives HistoryTracker
// so it's safe to use raw pointer here.
nsSHistory* mSHistory;
};
+ // Structure used in SetChildHistoryEntry
+ struct SwapEntriesData
+ {
+ nsDocShell* ignoreShell; // constant; the shell to ignore
+ nsISHEntry* destTreeRoot; // constant; the root of the dest tree
+ nsISHEntry* destTreeParent; // constant; the node under destTreeRoot
+ // whose children will correspond to aEntry
+ };
+
nsSHistory();
NS_DECL_ISUPPORTS
NS_DECL_NSISHISTORY
NS_DECL_NSISHISTORYINTERNAL
NS_DECL_NSIWEBNAVIGATION
// One time initialization method called upon docshell module construction
static nsresult Startup();
@@ -73,16 +82,60 @@ public:
static void UpdatePrefs();
// 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.
static uint32_t GetMaxTotalViewers() { return sHistoryMaxTotalViewers; }
+ // Get the root SHEntry from a given entry.
+ static nsISHEntry* GetRootSHEntry(nsISHEntry* aEntry);
+
+ // Callback prototype for WalkHistoryEntries.
+ // aEntry is the child history entry, aShell is its corresponding docshell,
+ // aChildIndex is the child's index in its parent entry, and aData is
+ // the opaque pointer passed to WalkHistoryEntries.
+ typedef nsresult(*WalkHistoryEntriesFunc)(nsISHEntry* aEntry,
+ nsDocShell* aShell,
+ int32_t aChildIndex,
+ void* aData);
+
+ // Clone a session history tree for subframe navigation.
+ // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
+ // for the entry with id |aCloneID|, which will be replaced with
+ // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
+ // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
+ // have that pointer updated to point to the cloned history entry.
+ // If aCloneChildren is true then the children of the entry with id
+ // |aCloneID| will be cloned into |aReplaceEntry|.
+ static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
+ nsDocShell* aSrcShell,
+ uint32_t aCloneID,
+ nsISHEntry* aReplaceEntry,
+ bool aCloneChildren,
+ nsISHEntry** aDestEntry);
+
+ // Child-walking callback for CloneAndReplace
+ static nsresult CloneAndReplaceChild(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aChildIndex, void* aData);
+
+
+ // Child-walking callback for SetHistoryEntry
+ static nsresult SetChildHistoryEntry(nsISHEntry* aEntry, nsDocShell* aShell,
+ int32_t aEntryIndex, void* aData);
+
+ // For each child of aRootEntry, find the corresponding docshell which is
+ // a child of aRootShell, and call aCallback. The opaque pointer aData
+ // is passed to the callback.
+ static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
+ nsDocShell* aRootShell,
+ WalkHistoryEntriesFunc aCallback,
+ void* aData);
+
private:
virtual ~nsSHistory();
friend class nsSHEnumerator;
friend class nsSHistoryObserver;
nsresult GetTransactionAtIndex(int32_t aIndex, nsISHTransaction** aResult);
nsresult LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry,
nsIDocShell* aRootDocShell, long aLoadType,