Commit bfdcd82c authored by Anny Gakhokidze's avatar Anny Gakhokidze
Browse files

Bug 1545474 - Part 2: Modify eviction of content viewers to accomodate session...

Bug 1545474 - Part 2: Modify eviction of content viewers to accomodate session history changes, r=peterv

When we need to evict content viewers, we group SHEntrySharedParentState,
corresponding to session history entries that need to be evicted, by their
content parent and send their id's to their corresponding parents for eviction.

Differential Revision: https://phabricator.services.mozilla.com/D32730

--HG--
extra : rebase_source : 17398d3ed2fdf5debba8224559c48f41b4cfbf2d
extra : source : 0feb286c5b85e6e2a0ac56058c596324ca28d817
extra : histedit_source : ba6c9eb5c6ce7013b80c407372eeab7146b07f51
parent c43df3d4
......@@ -48,6 +48,24 @@ void SHEntryChildShared::Remove(uint64_t aSharedID) {
}
}
void SHEntryChildShared::EvictContentViewers(
const nsTArray<uint64_t>& aToEvictSharedStateIDs) {
MOZ_ASSERT(sSHEntryChildSharedTable,
"we have content viewers to evict, but the table hasn't been "
"initialized yet");
for (auto iter = aToEvictSharedStateIDs.begin();
iter != aToEvictSharedStateIDs.end(); ++iter) {
RefPtr<SHEntryChildShared> shared = sSHEntryChildSharedTable->Get(*iter);
MOZ_ASSERT(shared, "shared entry can't be null");
nsCOMPtr<nsIContentViewer> viewer = shared->mContentViewer;
if (viewer) {
shared->SetContentViewer(nullptr);
shared->SyncPresentationState();
viewer->Destroy();
}
}
}
SHEntryChildShared::SHEntryChildShared(uint64_t aID) : mID(aID) {}
SHEntryChildShared::~SHEntryChildShared() {
......
......@@ -42,6 +42,8 @@ class SHEntryChildShared final : public nsIBFCacheEntry,
static uint64_t CreateSharedID() {
return nsContentUtils::GenerateProcessSpecificId(++sNextSharedID);
}
static void EvictContentViewers(
const nsTArray<uint64_t>& aToEvictSharedStateIDs);
NS_DECL_ISUPPORTS
NS_DECL_NSIBFCACHEENTRY
......
......@@ -57,6 +57,10 @@ class LegacySHEntry final : public nsSHEntry {
uint64_t GetSharedStateID() const { return mShared->GetID(); }
dom::SHEntrySharedParentState* GetSharedState() const {
return mShared.get();
}
private:
friend class SHEntryParent;
friend class ContentParent;
......
......@@ -8,6 +8,10 @@
#include "mozilla/dom/SHEntryParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "nsTHashtable.h"
#include "mozilla/Logging.h"
extern mozilla::LazyLogModule gSHistoryLog;
namespace mozilla {
namespace dom {
......@@ -131,8 +135,7 @@ bool SHistoryParent::RecvNotifyOnHistoryReload(bool* aOk) {
}
bool SHistoryParent::RecvEvictOutOfRangeContentViewers(int32_t aIndex) {
// FIXME Implement this!
return true;
return NS_SUCCEEDED(mHistory->EvictOutOfRangeContentViewers(aIndex));
}
bool SHistoryParent::RecvEvictAllContentViewers() {
......@@ -236,5 +239,97 @@ bool SHistoryParent::RecvEvict(nsTArray<PSHEntryParent*>&& aEntries) {
return true;
}
void LegacySHistory::EvictOutOfRangeWindowContentViewers(int32_t aIndex) {
if (aIndex < 0) {
return;
}
NS_ENSURE_TRUE_VOID(aIndex < Length());
// Calculate the range that's safe from eviction.
int32_t startSafeIndex, endSafeIndex;
WindowIndices(aIndex, &startSafeIndex, &endSafeIndex);
// See nsSHistory::EvictOutOfRangeWindowContentViewers for more comments.
MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug,
("EvictOutOfRangeWindowContentViewers(index=%d), "
"Length()=%d. Safe range [%d, %d]",
aIndex, Length(), startSafeIndex, endSafeIndex));
// Collect content viewers within safe range so we don't accidentally evict
// one of them if it appears outside this range.
nsTHashtable<nsUint64HashKey> safeSharedStateIDs;
for (int32_t i = startSafeIndex; i <= endSafeIndex; i++) {
RefPtr<LegacySHEntry> entry =
static_cast<LegacySHEntry*>(mEntries[i].get());
MOZ_ASSERT(entry);
safeSharedStateIDs.PutEntry(entry->GetSharedStateID());
}
// Iterate over entries that are not within safe range and save the IDs
// of shared state and content parents into a hashtable.
// Format of the hashtable: content parent -> list of shared state IDs
nsDataHashtable<nsPtrHashKey<PContentParent>, nsTHashtable<nsUint64HashKey>>
toEvict;
for (int32_t i = 0; i < Length(); i++) {
if (i >= startSafeIndex && i <= endSafeIndex) {
continue;
}
RefPtr<LegacySHEntry> entry =
static_cast<LegacySHEntry*>(mEntries[i].get());
dom::SHEntrySharedParentState* sharedParentState = entry->GetSharedState();
uint64_t id = entry->GetSharedStateID();
PContentParent* parent =
static_cast<SHEntrySharedParent*>(sharedParentState)
->GetContentParent();
MOZ_ASSERT(parent);
if (!safeSharedStateIDs.Contains(id)) {
nsTHashtable<nsUint64HashKey>& ids = toEvict.GetOrInsert(parent);
ids.PutEntry(id);
}
}
if (toEvict.Count() == 0) {
return;
}
// Remove dynamically created children from entries that will be evicted
// later. We are iterating over the mEntries (instead of toEvict) to gain
// access to each nsSHEntry because toEvict only contains content parents and
// IDs of SHEntrySharedParentState
// (It's important that the condition checks Length(), rather than a cached
// copy of Length(), because the length might change between iterations due to
// RemoveDynEntries call.)
for (int32_t i = 0; i < Length(); i++) {
RefPtr<LegacySHEntry> entry =
static_cast<LegacySHEntry*>(mEntries[i].get());
MOZ_ASSERT(entry);
uint64_t id = entry->GetSharedStateID();
if (!safeSharedStateIDs.Contains(id)) {
// When dropping bfcache, we have to remove associated dynamic entries as
// well.
int32_t index = GetIndexOfEntry(entry);
if (index != -1) {
RemoveDynEntries(index, entry);
}
}
}
// Iterate over the 'toEvict' hashtable to get the IDs of content viewers to
// evict for each parent
for (auto iter = toEvict.ConstIter(); !iter.Done(); iter.Next()) {
auto parent = iter.Key();
const nsTHashtable<nsUint64HashKey>& ids = iter.Data();
// Convert ids into an array because we don't have support for passing
// nsTHashtable over IPC
AutoTArray<uint64_t, 4> evictArray;
for (auto iter = ids.ConstIter(); !iter.Done(); iter.Next()) {
evictArray.AppendElement(iter.Get()->GetKey());
}
Unused << parent->SendEvictContentViewers(evictArray);
}
}
} // namespace dom
} // namespace mozilla
......@@ -26,6 +26,8 @@ class LegacySHistory final : public nsSHistory {
private:
virtual ~LegacySHistory() {}
void EvictOutOfRangeWindowContentViewers(int32_t aIndex) override;
public:
LegacySHistory(mozilla::dom::CanonicalBrowsingContext* aRootBC,
const nsID& aDocShellID);
......
......@@ -59,7 +59,7 @@ int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
// entries were touched, so that we can evict older entries first.
static uint32_t gTouchCounter = 0;
static LazyLogModule gSHistoryLog("nsSHistory");
LazyLogModule gSHistoryLog("nsSHistory");
#define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format)
......
......@@ -172,7 +172,7 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
// Evict content viewers in this window which don't lie in the "safe" range
// around aIndex.
void EvictOutOfRangeWindowContentViewers(int32_t aIndex);
virtual void EvictOutOfRangeWindowContentViewers(int32_t aIndex);
void EvictContentViewerForEntry(nsISHEntry* aEntry);
static void GloballyEvictContentViewers();
static void GloballyEvictAllContentViewers();
......@@ -190,17 +190,17 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
// otherwise comparison is done to aIndex - 1.
bool RemoveDuplicate(int32_t aIndex, bool aKeepNext);
protected:
// Length of mEntries.
int32_t Length() { return int32_t(mEntries.Length()); }
protected:
bool mIsRemote;
nsTArray<nsCOMPtr<nsISHEntry>> mEntries; // entries are never null
private:
// Track all bfcache entries and evict on expiration.
mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
nsTArray<nsCOMPtr<nsISHEntry>> mEntries; // entries are never null
int32_t mIndex; // -1 means "no index"
int32_t mRequestedIndex; // -1 means "no requested index"
......
......@@ -3758,6 +3758,12 @@ mozilla::ipc::IPCResult ContentChild::RecvDestroySHEntrySharedState(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvEvictContentViewers(
nsTArray<uint64_t>&& aToEvictSharedStateIDs) {
SHEntryChildShared::EvictContentViewers(std::move(aToEvictSharedStateIDs));
return IPC_OK();
}
already_AddRefed<nsIEventTarget> ContentChild::GetSpecificMessageEventTarget(
const Message& aMsg) {
switch (aMsg.type()) {
......
......@@ -691,6 +691,8 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvDestroySHEntrySharedState(const uint64_t& aID);
mozilla::ipc::IPCResult RecvEvictContentViewers(nsTArray<uint64_t>&& aToEvictSharedStateIDs);
#ifdef NIGHTLY_BUILD
// Fetch the current number of pending input events.
//
......
......@@ -842,6 +842,8 @@ child:
async DestroySHEntrySharedState(uint64_t aID);
async EvictContentViewers(uint64_t[] aToEvictSharedStateIDs);
parent:
async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment