Bug 1545474 - Part 3: Test eviction of content viewer entries, r=peterv

Add a new callback to session history listener to listen for content
viewers being evicted.

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

--HG--
extra : rebase_source : 29c8e9299af718e75029dfae4e937a3d4f18900d
extra : amend_source : 19e679c6d2721dd7e8d7da1ec7af3033439210b4
extra : source : 5e4d0ab9e52ce1e1d1684fdb59534a66e9f9d9b9
extra : histedit_source : 44e9cb7ba0589c79c03b2dc58ec4d0d4c0c98422
This commit is contained in:
Anny Gakhokidze 2019-05-22 16:19:19 -04:00
parent bfdcd82cdb
commit 7c10781235
15 changed files with 129 additions and 1 deletions

View File

@ -58,7 +58,7 @@ parent:
sync Evict(PSHEntry[] entry);
sync EnsureCorrectEntryAtCurrIndex(PSHEntry entry);
sync EvictContentViewersOrReplaceEntry(nullable PSHEntry newSHEntry, bool replace);
async NotifyListenersContentViewerEvicted(uint32_t numEvicted);
async __delete__();
};

View File

@ -53,16 +53,25 @@ void SHEntryChildShared::EvictContentViewers(
MOZ_ASSERT(sSHEntryChildSharedTable,
"we have content viewers to evict, but the table hasn't been "
"initialized yet");
uint32_t numEvictedSoFar = 0;
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) {
numEvictedSoFar++;
shared->SetContentViewer(nullptr);
shared->SyncPresentationState();
viewer->Destroy();
}
if (std::next(iter) == aToEvictSharedStateIDs.end() &&
numEvictedSoFar > 0) {
// This is the last shared object, so we should notify our
// listeners about any content viewers that were evicted.
// It does not matter which shared entry we will use for notifying.
shared->NotifyListenersContentViewerEvicted(numEvictedSoFar);
}
}
}
@ -134,6 +143,13 @@ void SHEntryChildShared::DropPresentationState() {
mEditorData = nullptr;
}
void SHEntryChildShared::NotifyListenersContentViewerEvicted(
uint32_t aNumEvicted) {
if (StaticPrefs::docshell_shistory_testing_bfevict() && mSHistory) {
mSHistory->SendNotifyListenersContentViewerEvicted(aNumEvicted);
}
}
nsresult SHEntryChildShared::SetContentViewer(nsIContentViewer* aViewer) {
MOZ_ASSERT(!aViewer || !mContentViewer,
"SHEntryShared already contains viewer");
@ -994,6 +1010,7 @@ void SHEntryChild::EvictContentViewer() {
if (viewer) {
// Drop the presentation state before destroying the viewer, so that
// document teardown is able to correctly persist the state.
mShared->NotifyListenersContentViewerEvicted();
SetContentViewer(nullptr);
SyncPresentationState();
viewer->Destroy();

View File

@ -45,6 +45,8 @@ class SHEntryChildShared final : public nsIBFCacheEntry,
static void EvictContentViewers(
const nsTArray<uint64_t>& aToEvictSharedStateIDs);
void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted = 1);
NS_DECL_ISUPPORTS
NS_DECL_NSIBFCACHEENTRY

View File

@ -239,6 +239,12 @@ bool SHistoryParent::RecvEvict(nsTArray<PSHEntryParent*>&& aEntries) {
return true;
}
bool SHistoryParent::RecvNotifyListenersContentViewerEvicted(
uint32_t aNumEvicted) {
mHistory->NotifyListenersContentViewerEvicted(aNumEvicted);
return true;
}
void LegacySHistory::EvictOutOfRangeWindowContentViewers(int32_t aIndex) {
if (aIndex < 0) {
return;

View File

@ -80,6 +80,7 @@ class SHistoryParent final : public PSHistoryParent {
bool RecvEvict(nsTArray<PSHEntryParent*>&& aEntries);
bool RecvEnsureCorrectEntryAtCurrIndex(PSHEntryParent* aEntry);
bool RecvEvictContentViewersOrReplaceEntry(PSHEntryParent* aNewSHEntry, bool aReplace);
bool RecvNotifyListenersContentViewerEvicted(uint32_t aNumEvicted);
RefPtr<CanonicalBrowsingContext> mContext;
RefPtr<LegacySHistory> mHistory;

View File

@ -65,4 +65,14 @@ interface nsISHistoryListener : nsISupports
* about pages) and when history.replaceState is called.
*/
void OnHistoryReplaceEntry();
/**
* Called whenever a content viewer is evicted. A content viewer is evicted
* whenever a bfcache entry has timed out or the number of total content
* viewers has exceeded the global max. This is used for testing only.
*
* @param aNumEvicted - number of content viewers evicted
*/
void OnContentViewerEvicted(in unsigned long aNumEvicted);
};

View File

@ -1038,6 +1038,7 @@ nsLegacySHEntry::CreateLoadInfo(nsDocShellLoadState** aLoadState) {
void nsSHEntry::EvictContentViewer() {
nsCOMPtr<nsIContentViewer> viewer = GetContentViewer();
if (viewer) {
mShared->NotifyListenersContentViewerEvicted();
// Drop the presentation state before destroying the viewer, so that
// document teardown is able to correctly persist the state.
SetContentViewer(nullptr);

View File

@ -52,6 +52,13 @@ void dom::SHEntrySharedParentState::CopyFrom(
mLastTouched = aEntry->mLastTouched;
}
void dom::SHEntrySharedParentState::NotifyListenersContentViewerEvicted() {
if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
RefPtr<nsSHistory> nsshistory = static_cast<nsSHistory*>(shistory.get());
nsshistory->NotifyListenersContentViewerEvicted(1);
}
}
dom::SHEntrySharedChildState::SHEntrySharedChildState()
: mSaveLayoutState(true) {}

View File

@ -51,6 +51,8 @@ class SHEntrySharedParentState {
uint64_t GetID() const { return mID; }
void NotifyListenersContentViewerEvicted();
protected:
friend class nsSHEntry;

View File

@ -179,6 +179,7 @@ void nsSHistory::EvictContentViewerForEntry(nsISHEntry* aEntry) {
// Drop the presentation state before destroying the viewer, so that
// document teardown is able to correctly persist the state.
NotifyListenersContentViewerEvicted(1);
aEntry->SetContentViewer(nullptr);
aEntry->SyncPresentationState();
viewer->Destroy();
@ -745,6 +746,10 @@ nsSHistory::AddSHistoryListener(nsISHistoryListener* aListener) {
return NS_OK;
}
void nsSHistory::NotifyListenersContentViewerEvicted(uint32_t aNumEvicted) {
NOTIFY_LISTENERS(OnContentViewerEvicted, (aNumEvicted));
}
NS_IMETHODIMP
nsSHistory::RemoveSHistoryListener(nsISHistoryListener* aListener) {
// Make sure the listener that wants to be removed is the

View File

@ -143,6 +143,7 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex,
int32_t* aOutEndIndex);
void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted);
protected:
virtual ~nsSHistory();

View File

@ -7,6 +7,7 @@ support-files =
bug343515_pg3_1_1.html
bug343515_pg3_2.html
[browser_test_bfcache_eviction.js]
[browser_bug343515.js]
[browser_test-content-chromeflags.js]
tags = openwindow

View File

@ -0,0 +1,63 @@
add_task(async function() {
// We don't want the number of total viewers to be calculated by the available size
// for this test case. Instead, fix the number of viewers.
await SpecialPowers.pushPrefEnv({
set: [
["browser.sessionhistory.max_total_viewers", 3],
["docshell.shistory.testing.bfevict", true],
],
});
// 1. Open a tab
var testPage =
"data:text/html,<html id='html1'><body id='body1'></body></html>";
await BrowserTestUtils.withNewTab({ gBrowser, url: testPage }, async function(
browser
) {
// 2. Add a promise that will be resolved when the 'content viewer evicted' event goes off
let testDone = {};
testDone.promise = new Promise(resolve => {
testDone.resolve = resolve;
});
let legacySHistory = browser.browsingContext.sessionHistory;
// 3. Register a session history listener to listen for a 'content viewer evicted' event.
let historyListener = {
OnContentViewerEvicted() {
ok(
true,
"History listener got called after a content viewer was evicted"
);
legacySHistory.removeSHistoryListener(historyListener);
// 6. Resolve the promise when we got our 'content viewer evicted' event
testDone.resolve();
},
QueryInterface: ChromeUtils.generateQI([
Ci.nsISHistoryListener,
Ci.nsISupportsWeakReference,
]),
};
legacySHistory.addSHistoryListener(historyListener);
// 4. Open a second tab
testPage = `data:text/html,<html id='html1'><body id='body1'>I am a second tab!</body></html>`;
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
// 5. Navigate the first tab to 4 different pages.
// We should get 1 content viewer evicted because it will be outside of the range.
// If we have the following pages in our session history: P1 P2 P3 P4 P5
// and we are currently at P5, then P1 is outside of the range
// (it is more than 3 entries away from current entry) and thus will be evicted.
for (var i = 0; i < 4; i++) {
testPage = `data:text/html,<html id='html1'><body id='body1'>${i}</body></html>`;
let pagePromise = BrowserTestUtils.browserLoaded(browser);
await BrowserTestUtils.loadURI(browser, testPage);
await pagePromise;
}
// 7. Wait for 'content viewer evicted' event to go off
await testDone.promise;
// 8. Close the second tab
BrowserTestUtils.removeTab(tab2);
});
});

View File

@ -1234,6 +1234,17 @@
value: true
mirror: always
#---------------------------------------------------------------------------
# Prefs starting with "docshell."
#---------------------------------------------------------------------------
# Used to indicate whether session history listeners should be notified
# about content viewer eviction. Used only for testing.
- name: docshell.shistory.testing.bfevict
type: bool
value: false
mirror: always
#---------------------------------------------------------------------------
# Prefs starting with "dom."
#---------------------------------------------------------------------------

View File

@ -36,6 +36,7 @@ pref_groups = [
'content',
'device',
'devtools',
'docshell',
'dom',
'editor',
'extensions',