gecko-dev/docshell/shistory/SHistoryChild.cpp
Peter Van der Beken b6d4b5e93c Bug 1588491 - Associate session history entries with a session history object from creation. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D49170

--HG--
extra : rebase_source : bc4c6e8cea0de7e85ae78ffb3ce09a7465e61b4a
extra : amend_source : 956bf8fa227e613b7f816dd5af9029ed24ba2c46
extra : source : 2d4b06fe1f429eec844cdeba8a7faaa63bfc25ab
extra : histedit_source : a2345826c7f94311ee57d805bf3ac9bed2e12ea7
2019-10-17 20:03:49 +02:00

388 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SHistoryChild.h"
#include "SHEntryChild.h"
#include "nsISHistoryListener.h"
#define CONTENT_VIEWER_TIMEOUT_SECONDS \
"browser.sessionhistory.contentViewerTimeout"
// Default this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30 * 60)
namespace mozilla {
namespace dom {
void SHistoryChild::HistoryTracker::NotifyExpired(SHEntryChildShared* aObj) {
RemoveObject(aObj);
mSHistory->EvictExpiredContentViewerForEntry(aObj);
}
SHistoryChild::SHistoryChild(BrowsingContext* aRootBC)
: mRootDocShell(static_cast<nsDocShell*>(aRootBC->GetDocShell())),
mIPCActorDeleted(false) {
// Bind mHistoryTracker's event target to the tabGroup for aRootBC.
// Maybe move this to ChildSHistory?
nsCOMPtr<nsPIDOMWindowOuter> win = aRootBC->GetDOMWindow();
if (win) {
// Seamonkey moves shistory between <xul:browser>s when restoring a tab.
// Let's try not to break our friend too badly...
if (mHistoryTracker) {
NS_WARNING(
"Change the root docshell of a shistory is unsafe and "
"potentially problematic.");
mHistoryTracker->AgeAllGenerations();
}
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(win);
mHistoryTracker = mozilla::MakeUnique<SHistoryChild::HistoryTracker>(
this,
mozilla::Preferences::GetUint(CONTENT_VIEWER_TIMEOUT_SECONDS,
CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT),
global->EventTargetFor(mozilla::TaskCategory::Other));
}
}
NS_IMPL_ADDREF(SHistoryChild)
NS_IMETHODIMP_(MozExternalRefCountType) SHistoryChild::Release() {
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
NS_ASSERT_OWNINGTHREAD(SHEntryChild);
nsrefcnt count = --mRefCnt;
NS_LOG_RELEASE(this, count, "SHistoryChild");
if (count == 0) {
mRefCnt = 1; /* stabilize */
delete this;
return 0;
}
if (count == 1 && !mIPCActorDeleted) {
Unused << Send__delete__(this);
}
return count;
}
NS_IMPL_QUERY_INTERFACE(SHistoryChild, nsISHistory, nsISupportsWeakReference)
NS_IMETHODIMP
SHistoryChild::GetCount(int32_t* aCount) {
return SendGetCount(aCount) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::GetIndex(int32_t* aIndex) {
return SendGetIndex(aIndex) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::SetIndex(int32_t aIndex) {
nsresult rv;
return SendSetIndex(aIndex, &rv) ? rv : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::GetRequestedIndex(int32_t* aRequestedIndex) {
return SendGetRequestedIndex(aRequestedIndex) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP_(void)
SHistoryChild::InternalSetRequestedIndex(int32_t aRequestedIndex) {
SendInternalSetRequestedIndex(aRequestedIndex);
}
NS_IMETHODIMP
SHistoryChild::GetEntryAtIndex(int32_t aIndex, nsISHEntry** aResult) {
nsresult rv;
RefPtr<CrossProcessSHEntry> entry;
if (!SendGetEntryAtIndex(aIndex, &rv, &entry)) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_SUCCESS(rv, rv);
*aResult = entry ? do_AddRef(entry->ToSHEntryChild()).take() : nullptr;
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::PurgeHistory(int32_t aNumEntries) {
nsresult rv;
if (!SendPurgeHistory(aNumEntries, &rv)) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_SUCCESS(rv, rv);
if (mRootDocShell) {
mRootDocShell->HistoryPurged(aNumEntries);
}
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::AddSHistoryListener(nsISHistoryListener* aListener) {
NS_ENSURE_ARG_POINTER(aListener);
// Check if the listener supports Weak Reference. This is a must.
// This listener functionality is used by embedders and we want to
// have the right ownership with who ever listens to SHistory
nsWeakPtr listener = do_GetWeakReference(aListener);
if (!listener) {
return NS_ERROR_FAILURE;
}
mListeners.AppendElementUnlessExists(listener);
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::RemoveSHistoryListener(nsISHistoryListener* aListener) {
// Make sure the listener that wants to be removed is the
// one we have in store.
nsWeakPtr listener = do_GetWeakReference(aListener);
mListeners.RemoveElement(listener);
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::ReloadCurrentEntry() {
LoadSHEntryResult loadResult;
if (!SendReloadCurrentEntry(&loadResult)) {
return NS_ERROR_FAILURE;
}
if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
return loadResult;
}
return LoadURI(loadResult);
}
NS_IMETHODIMP
SHistoryChild::GotoIndex(int32_t aIndex) {
LoadSHEntryResult loadResult;
if (!SendGotoIndex(aIndex, &loadResult)) {
return NS_ERROR_FAILURE;
}
if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
return loadResult;
}
return LoadURI(loadResult);
}
NS_IMETHODIMP_(int32_t)
SHistoryChild::GetIndexOfEntry(nsISHEntry* aEntry) {
int32_t index;
if (!SendGetIndexOfEntry(static_cast<SHEntryChild*>(aEntry), &index)) {
return 0;
}
return index;
}
NS_IMETHODIMP
SHistoryChild::AddEntry(nsISHEntry* aEntry, bool aPersist) {
NS_ENSURE_ARG(aEntry);
nsresult rv;
int32_t entriesPurged;
if (!SendAddEntry(static_cast<SHEntryChild*>(aEntry), aPersist, &rv,
&entriesPurged)) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_SUCCESS(rv, rv);
if (mRootDocShell) {
aEntry->SetDocshellID(mRootDocShell->HistoryID());
if (entriesPurged > 0) {
mRootDocShell->HistoryPurged(entriesPurged);
}
}
return NS_OK;
}
NS_IMETHODIMP_(void)
SHistoryChild::ClearRootBrowsingContext() { mRootDocShell = nullptr; }
NS_IMETHODIMP
SHistoryChild::UpdateIndex(void) {
return SendUpdateIndex() ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::ReplaceEntry(int32_t aIndex, nsISHEntry* aReplaceEntry) {
nsresult rv;
if (!SendReplaceEntry(aIndex, static_cast<SHEntryChild*>(aReplaceEntry),
&rv)) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::NotifyOnHistoryReload(bool* _retval) {
return SendNotifyOnHistoryReload(_retval) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::EvictOutOfRangeContentViewers(int32_t aIndex) {
// FIXME Need to get out of range entries and entries that are safe (to
// compare content viewers so we don't evict live content viewers).
return SendEvictOutOfRangeContentViewers(aIndex) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
SHistoryChild::EvictExpiredContentViewerForEntry(nsIBFCacheEntry* aBFEntry) {
SHEntryChildShared* shared = static_cast<SHEntryChildShared*>(aBFEntry);
RefPtr<CrossProcessSHEntry> entry;
int32_t index;
if (!SendFindEntryForBFCache(shared->GetID(), false, &entry, &index)) {
return NS_ERROR_FAILURE;
}
RefPtr<SHEntryChild> shEntry;
if (entry && (shEntry = entry->ToSHEntryChild())) {
shEntry->EvictContentViewer();
SendEvict(nsTArray<PSHEntryChild*>({shEntry.get()}));
}
return NS_OK;
}
NS_IMETHODIMP
SHistoryChild::EvictAllContentViewers(void) {
nsTArray<RefPtr<CrossProcessSHEntry>> entries;
if (!SendGetAllEntries(&entries)) {
return NS_ERROR_FAILURE;
}
// Keep a strong reference to all the entries, we're going to send the array
// back to the parent!
nsTArray<RefPtr<SHEntryChild>> shEntries(entries.Length());
for (RefPtr<CrossProcessSHEntry>& entry : entries) {
RefPtr<SHEntryChild> shEntry = entry->ToSHEntryChild();
shEntry->EvictContentViewer();
shEntries.AppendElement(shEntry.forget());
}
nsTArray<PSHEntryChild*> pshEntries;
pshEntries.AppendElements(shEntries);
SendEvict(pshEntries);
return NS_OK;
}
NS_IMETHODIMP_(void)
SHistoryChild::EvictContentViewersOrReplaceEntry(nsISHEntry* aNewSHEntry,
bool aReplace) {
SendEvictContentViewersOrReplaceEntry(static_cast<SHEntryChild*>(aNewSHEntry),
aReplace);
}
NS_IMETHODIMP_(void)
SHistoryChild::AddToExpirationTracker(nsIBFCacheEntry* aBFEntry) {
RefPtr<SHEntryChildShared> entry = static_cast<SHEntryChildShared*>(aBFEntry);
if (mHistoryTracker && entry) {
mHistoryTracker->AddObject(entry);
}
}
NS_IMETHODIMP_(void)
SHistoryChild::RemoveFromExpirationTracker(nsIBFCacheEntry* aBFEntry) {
RefPtr<SHEntryChildShared> entry = static_cast<SHEntryChildShared*>(aBFEntry);
MOZ_ASSERT(mHistoryTracker && !mHistoryTracker->IsEmpty());
if (mHistoryTracker && entry) {
mHistoryTracker->RemoveObject(entry);
}
}
NS_IMETHODIMP_(void)
SHistoryChild::RemoveDynEntries(int32_t aIndex, nsISHEntry* aEntry) {
SendRemoveDynEntries(aIndex, static_cast<SHEntryChild*>(aEntry));
}
NS_IMETHODIMP_(void)
SHistoryChild::EnsureCorrectEntryAtCurrIndex(nsISHEntry* aEntry) {
SendEnsureCorrectEntryAtCurrIndex(static_cast<SHEntryChild*>(aEntry));
}
NS_IMETHODIMP_(void)
SHistoryChild::RemoveDynEntriesForBFCacheEntry(nsIBFCacheEntry* aBFEntry) {
RefPtr<CrossProcessSHEntry> entry;
int32_t index;
if (!SendFindEntryForBFCache(
static_cast<SHEntryChildShared*>(aBFEntry)->GetID(), true, &entry,
&index)) {
return;
}
RefPtr<SHEntryChild> shEntry;
if (entry && (shEntry = entry->ToSHEntryChild())) {
RemoveDynEntries(index, shEntry);
}
}
NS_IMETHODIMP_(void)
SHistoryChild::RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex) {
bool didRemove = false;
if (SendRemoveEntries(aIDs, aStartIndex, &didRemove) && didRemove &&
mRootDocShell) {
mRootDocShell->DispatchLocationChangeEvent();
}
}
NS_IMETHODIMP_(void)
SHistoryChild::RemoveFrameEntries(nsISHEntry* aEntry) {
SendRemoveFrameEntries(static_cast<SHEntryChild*>(aEntry));
}
NS_IMETHODIMP
SHistoryChild::Reload(uint32_t aReloadFlags) {
LoadSHEntryResult loadResult;
if (!SendReload(aReloadFlags, &loadResult)) {
return NS_ERROR_FAILURE;
}
if (loadResult.type() == LoadSHEntryResult::Tnsresult) {
return loadResult;
}
return LoadURI(loadResult);
}
NS_IMETHODIMP
SHistoryChild::CreateEntry(nsISHEntry** aEntry) {
uint64_t sharedID = SHEntryChildShared::CreateSharedID();
RefPtr<SHEntryChild> entry = static_cast<SHEntryChild*>(
Manager()->SendPSHEntryConstructor(this, sharedID));
if (!entry) {
return NS_ERROR_FAILURE;
}
entry.forget(aEntry);
return NS_OK;
}
nsresult SHistoryChild::LoadURI(LoadSHEntryData& aLoadData) {
nsCOMPtr<nsIDocShell> docShell = aLoadData.browsingContext()->GetDocShell();
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
RefPtr<SHEntryChild> entry;
if (aLoadData.shEntry()) {
entry = aLoadData.shEntry()->ToSHEntryChild();
}
// FIXME Should this be sent through IPC?
aLoadData.loadState()->SetSHEntry(entry);
return docShell->LoadURI(aLoadData.loadState(), false);
}
} // namespace dom
} // namespace mozilla