Bug 1658341 - Implement SessionHistory::AddChild/RemoveChild/ReplaceChild. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D86574
This commit is contained in:
Peter Van der Beken 2020-08-13 08:48:48 +00:00
Родитель a35af34bfa
Коммит 5e736113a2
2 изменённых файлов: 118 добавлений и 92 удалений

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

@ -430,9 +430,13 @@ SessionHistoryEntry::SetStateData(nsIStructuredCloneContainer* aStateData) {
return NS_OK;
}
const nsID& SessionHistoryEntry::DocshellID() const {
return mSharedInfo->mDocShellID;
}
NS_IMETHODIMP
SessionHistoryEntry::GetDocshellID(nsID& aDocshellID) {
aDocshellID = mSharedInfo->mDocShellID;
aDocshellID = DocshellID();
return NS_OK;
}
@ -686,13 +690,22 @@ SessionHistoryEntry::SetLoadTypeAsHistory() {
NS_IMETHODIMP
SessionHistoryEntry::AddChild(nsISHEntry* aChild, int32_t aOffset,
bool aUseRemoteSubframes) {
nsCOMPtr<SessionHistoryEntry> child = do_QueryInterface(aChild);
MOZ_ASSERT(child);
AddChild(child, aOffset, aUseRemoteSubframes);
return NS_OK;
}
void SessionHistoryEntry::AddChild(SessionHistoryEntry* aChild, int32_t aOffset,
bool aUseRemoteSubframes) {
if (aChild) {
NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE);
aChild->SetParent(this);
}
if (aOffset < 0) {
mChildren.AppendObject(aChild);
return NS_OK;
mChildren.AppendElement(aChild);
return;
}
//
@ -701,69 +714,65 @@ SessionHistoryEntry::AddChild(nsISHEntry* aChild, int32_t aOffset,
// Later frames in the child list may load faster and get appended
// before earlier frames, causing session history to be scrambled.
// By growing the list here, they are added to the right position.
//
// Assert that aOffset will not be so high as to grow us a lot.
//
NS_ASSERTION(aOffset < (mChildren.Count() + 1023), "Large frames array!\n");
bool newChildIsDyn = aChild ? aChild->IsDynamicallyAdded() : false;
int32_t length = mChildren.Length();
// Assert that aOffset will not be so high as to grow us a lot.
NS_ASSERTION(aOffset < length + 1023, "Large frames array!\n");
// If the new child is dynamically added, try to add it to aOffset, but if
// there are non-dynamically added children, the child must be after those.
if (newChildIsDyn) {
if (aChild && aChild->IsDynamicallyAdded()) {
int32_t lastNonDyn = aOffset - 1;
for (int32_t i = aOffset; i < mChildren.Count(); ++i) {
nsISHEntry* entry = mChildren[i];
for (int32_t i = aOffset; i < length; ++i) {
SessionHistoryEntry* entry = mChildren[i];
if (entry) {
if (entry->IsDynamicallyAdded()) {
break;
} else {
}
lastNonDyn = i;
}
}
// If aOffset is larger than Length(), we must first truncate the array.
if (aOffset > length) {
mChildren.SetLength(aOffset);
}
// InsertObjectAt allows only appending one object.
// If aOffset is larger than Count(), we must first manually
// set the capacity.
if (aOffset > mChildren.Count()) {
mChildren.SetCount(aOffset);
mChildren.InsertElementAt(lastNonDyn + 1, aChild);
return;
}
if (!mChildren.InsertObjectAt(aChild, lastNonDyn + 1)) {
NS_WARNING("Adding a child failed!");
aChild->SetParent(nullptr);
return NS_ERROR_FAILURE;
}
} else {
// If the new child isn't dynamically added, it should be set to aOffset.
// If there are dynamically added children before that, those must be
// moved to be after aOffset.
if (mChildren.Count() > 0) {
int32_t start = std::min(mChildren.Count() - 1, aOffset);
// If there are dynamically added children before that, those must be moved
// to be after aOffset.
if (length > 0) {
int32_t start = std::min(length - 1, aOffset);
int32_t dynEntryIndex = -1;
nsISHEntry* dynEntry = nullptr;
DebugOnly<SessionHistoryEntry*> dynEntry = nullptr;
for (int32_t i = start; i >= 0; --i) {
nsISHEntry* entry = mChildren[i];
SessionHistoryEntry* entry = mChildren[i];
if (entry) {
if (entry->IsDynamicallyAdded()) {
dynEntryIndex = i;
dynEntry = entry;
} else {
if (!entry->IsDynamicallyAdded()) {
break;
}
dynEntryIndex = i;
dynEntry = entry;
}
}
if (dynEntry) {
nsCOMArray<nsISHEntry> tmp;
tmp.SetCount(aOffset - dynEntryIndex + 1);
mChildren.InsertObjectsAt(tmp, dynEntryIndex);
if (dynEntryIndex >= 0) {
mChildren.InsertElementsAt(dynEntryIndex, aOffset - dynEntryIndex + 1);
NS_ASSERTION(mChildren[aOffset + 1] == dynEntry, "Whaat?");
}
}
// Make sure there isn't anything at aOffset.
if (aOffset < mChildren.Count()) {
nsISHEntry* oldChild = mChildren[aOffset];
if ((uint32_t)aOffset < mChildren.Length()) {
SessionHistoryEntry* oldChild = mChildren[aOffset];
if (oldChild && oldChild != aChild) {
// Under Fission, this can happen when a network-created iframe starts
// out in-process, moves out-of-process, and then switches back. At that
@ -780,38 +789,42 @@ SessionHistoryEntry::AddChild(nsISHEntry* aChild, int32_t aOffset,
}
}
mChildren.ReplaceObjectAt(aChild, aOffset);
}
return NS_OK;
mChildren.ReplaceElementAt(aOffset, aChild);
}
NS_IMETHODIMP
SessionHistoryEntry::RemoveChild(nsISHEntry* aChild) {
NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE);
nsCOMPtr<SessionHistoryEntry> child = do_QueryInterface(aChild);
MOZ_ASSERT(child);
RemoveChild(child);
return NS_OK;
}
void SessionHistoryEntry::RemoveChild(SessionHistoryEntry* aChild) {
bool childRemoved = false;
if (aChild->IsDynamicallyAdded()) {
childRemoved = mChildren.RemoveObject(aChild);
childRemoved = mChildren.RemoveElement(aChild);
} else {
int32_t index = mChildren.IndexOfObject(aChild);
int32_t index = mChildren.IndexOf(aChild);
if (index >= 0) {
// Other alive non-dynamic child docshells still keep mChildOffset,
// so we don't want to change the indices here.
mChildren.ReplaceObjectAt(nullptr, index);
mChildren.ReplaceElementAt(index, nullptr);
childRemoved = true;
}
}
if (childRemoved) {
aChild->SetParent(nullptr);
// reduce the child count, i.e. remove empty children at the end
for (int32_t i = mChildren.Count() - 1; i >= 0 && !mChildren[i]; --i) {
if (!mChildren.RemoveObjectAt(i)) {
break;
for (int32_t i = mChildren.Length() - 1; i >= 0 && !mChildren[i]; --i) {
mChildren.RemoveElementAt(i);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
@ -829,24 +842,27 @@ SessionHistoryEntry::GetChildSHEntryIfHasNoDynamicallyAddedChild(
NS_IMETHODIMP
SessionHistoryEntry::ReplaceChild(nsISHEntry* aNewChild) {
NS_ENSURE_STATE(aNewEntry);
NS_ENSURE_STATE(aNewChild);
nsID docshellID;
aNewEntry->GetDocshellID(docshellID);
nsCOMPtr<SessionHistoryEntry> newChild = do_QueryInterface(aNewChild);
MOZ_ASSERT(newChild);
return ReplaceChild(newChild) ? NS_OK : NS_ERROR_FAILURE;
}
for (int32_t i = 0; i < mChildren.Count(); ++i) {
if (mChildren[i]) {
nsID childDocshellID;
nsresult rv = mChildren[i]->GetDocshellID(childDocshellID);
NS_ENSURE_SUCCESS(rv, rv);
if (docshellID == childDocshellID) {
bool SessionHistoryEntry::ReplaceChild(SessionHistoryEntry* aNewChild) {
const nsID& docshellID = aNewChild->DocshellID();
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
if (mChildren[i] && docshellID == mChildren[i]->DocshellID()) {
mChildren[i]->SetParent(nullptr);
mChildren.ReplaceObjectAt(aNewEntry, i);
return aNewEntry->SetParent(this);
mChildren.ReplaceElementAt(i, aNewChild);
aNewChild->SetParent(this);
return true;
}
}
}
return NS_ERROR_FAILURE;
return false;
}
NS_IMETHODIMP_(void)

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

@ -109,6 +109,14 @@ class SessionHistoryEntry : public nsISHEntry {
const SessionHistoryInfo& Info() const { return *mInfo; }
void AddChild(SessionHistoryEntry* aChild, int32_t aOffset,
bool aUseRemoteSubframes);
void RemoveChild(SessionHistoryEntry* aChild);
// Finds the child with the same docshell ID as aNewChild, replaces it with
// aNewChild and returns true. If there is no child with the same docshell ID
// then it returns false.
bool ReplaceChild(SessionHistoryEntry* aNewChild);
// Get an entry based on SessionHistoryInfo's Id. Parent process only.
static SessionHistoryEntry* GetByInfoId(uint64_t aId);
@ -118,6 +126,8 @@ class SessionHistoryEntry : public nsISHEntry {
private:
virtual ~SessionHistoryEntry();
const nsID& DocshellID() const;
UniquePtr<SessionHistoryInfo> mInfo;
RefPtr<SHEntrySharedParentState> mSharedInfo;
nsISHEntry* mParent = nullptr;