author | Peter Van der Beken <peterv@propagandism.org> |
Thu, 17 Oct 2019 20:03:49 +0200 | |
changeset 500513 | 9a60f8caece89baa705f4f54862eff3b996a6b22 |
parent 500512 | 71bcae2d0e17b19d2eddc9cdfa1fcfa7699d503a |
child 500514 | 13da9115c96bb79d33ff7bb56a170c9834157bf6 |
push id | 114165 |
push user | pvanderbeken@mozilla.com |
push date | Wed, 06 Nov 2019 09:18:40 +0000 |
treeherder | mozilla-inbound@db1ddab2985d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1588491 |
milestone | 72.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -11324,17 +11324,23 @@ nsresult nsDocShell::AddToSessionHistory entry = mOSHE; if (entry) { entry->ClearEntry(); } } // Create a new entry if necessary. if (!entry) { - entry = components::SHEntry::Create(); + nsCOMPtr<nsIWebNavigation> webnav = do_QueryInterface(root); + NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE); + + RefPtr<ChildSHistory> shistory = webnav->GetSessionHistory(); + NS_ENSURE_TRUE(shistory, NS_ERROR_FAILURE); + + shistory->LegacySHistory()->CreateEntry(getter_AddRefs(entry)); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } } // Get the post data & referrer nsCOMPtr<nsIInputStream> inputStream;
--- a/docshell/build/components.conf +++ b/docshell/build/components.conf @@ -39,27 +39,16 @@ if buildconfig.substs['MOZ_WIDGET_TOOLKI Headers = ['/docshell/build/nsDocShellModule.h'] InitFunc = 'mozilla::InitDocShellModule' UnloadFunc = 'mozilla::UnloadDocShellModule' Classes = [ { - 'name': 'SHEntry', - 'cid': '{bfd1a791-ad9f-11d3-bdc7-0050040a9b44}', - 'contract_ids': ['@mozilla.org/browser/session-history-entry;1'], - 'type': 'nsISHEntry', - 'constructor': 'mozilla::dom::CreateSHEntry', - 'headers': [ - 'nsISHEntry.h', - 'mozilla/dom/ChildSHistory.h', - ], - }, - { 'name': 'DocLoader', 'cid': '{057b04d0-0ccf-11d2-beba-00805f8a66dc}', 'contract_ids': ['@mozilla.org/docloaderservice;1'], 'type': 'nsDocLoader', 'headers': ['nsDocLoader.h'], 'init_method': 'Init', }, {
--- a/docshell/shistory/ChildSHistory.cpp +++ b/docshell/shistory/ChildSHistory.cpp @@ -120,21 +120,10 @@ nsISupports* ChildSHistory::GetParentObj RefPtr<ContentFrameMessageManager> mm; if (mDocShell) { mm = mDocShell->GetMessageManager(); } // else we must be unlinked... can that happen here? return ToSupports(mm); } -already_AddRefed<nsISHEntry> CreateSHEntry() { - uint64_t sharedID = SHEntryChildShared::CreateSharedID(); - if (XRE_IsContentProcess() && StaticPrefs::fission_sessionHistoryInParent()) { - return do_AddRef(static_cast<SHEntryChild*>( - ContentChild::GetSingleton()->SendPSHEntryConstructor(sharedID))); - } - - nsCOMPtr<nsISHEntry> entry = new nsLegacySHEntry(sharedID); - return entry.forget(); -} - } // namespace dom } // namespace mozilla
--- a/docshell/shistory/ChildSHistory.h +++ b/docshell/shistory/ChildSHistory.h @@ -103,14 +103,12 @@ class ChildSHistory : public nsISupports int32_t mOffset; }; RefPtr<nsDocShell> mDocShell; nsCOMPtr<nsISHistory> mHistory; mozilla::LinkedList<PendingAsyncHistoryNavigation> mPendingNavigations; }; -already_AddRefed<nsISHEntry> CreateSHEntry(); - } // namespace dom } // namespace mozilla #endif /* mozilla_dom_ChildSHistory_h */
--- a/docshell/shistory/SHEntryChild.cpp +++ b/docshell/shistory/SHEntryChild.cpp @@ -54,17 +54,21 @@ void SHEntryChildShared::EvictContentVie const nsTArray<uint64_t>& aToEvictSharedStateIDs) { 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"); + if (!shared) { + // This can happen if we've created an entry in the parent, and have never + // sent it over IPC to the child. + continue; + } nsCOMPtr<nsIContentViewer> viewer = shared->mContentViewer; if (viewer) { numEvictedSoFar++; shared->SetContentViewer(nullptr); shared->SyncPresentationState(); viewer->Destroy(); } if (std::next(iter) == aToEvictSharedStateIDs.end() && @@ -584,17 +588,17 @@ SHEntryChild::Create( ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP SHEntryChild::Clone(nsISHEntry** aResult) { NS_IF_ADDREF(*aResult = static_cast<SHEntryChild*>( ContentChild::GetSingleton()->SendPSHEntryConstructor( - this))); + mShared->mSHistory, this))); return NS_OK; } NS_IMETHODIMP SHEntryChild::GetParent(nsISHEntry** aResult) { RefPtr<CrossProcessSHEntry> parent; if (!SendGetParent(&parent)) { return NS_ERROR_FAILURE; @@ -950,30 +954,22 @@ SHEntryChild::GetLastTouched(uint32_t* a } NS_IMETHODIMP SHEntryChild::SetLastTouched(uint32_t aLastTouched) { return SendSetLastTouched(aLastTouched) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP -SHEntryChild::GetSHistory(nsISHistory** aSHistory) { +SHEntryChild::GetShistory(nsISHistory** aSHistory) { *aSHistory = do_AddRef(mShared->mSHistory).take(); return NS_OK; } NS_IMETHODIMP -SHEntryChild::SetSHistory(nsISHistory* aSHistory) { - // mSHistory can not be changed once it's set - MOZ_ASSERT(!mShared->mSHistory || (mShared->mSHistory == aSHistory)); - mShared->mSHistory = static_cast<SHistoryChild*>(aSHistory); - return NS_OK; -} - -NS_IMETHODIMP SHEntryChild::SetLoadTypeAsHistory() { return SendSetLoadTypeAsHistory() ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP SHEntryChild::GetPersist(bool* aPersist) { return SendGetPersist(aPersist) ? NS_OK : NS_ERROR_FAILURE; }
--- a/docshell/shistory/SHEntryParent.cpp +++ b/docshell/shistory/SHEntryParent.cpp @@ -9,27 +9,34 @@ #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/MaybeNewPSHEntry.h" #include "nsStructuredCloneContainer.h" namespace mozilla { namespace dom { SHEntrySharedParent::SHEntrySharedParent(PContentParent* aContentParent, + LegacySHistory* aSHistory, uint64_t aSharedID) - : SHEntrySharedParentState(aSharedID), mContentParent(aContentParent) {} + : SHEntrySharedParentState(aSHistory, aSharedID), + mContentParent(aContentParent) {} void SHEntrySharedParent::Destroy() { if (mContentParent && !static_cast<ContentParent*>(mContentParent.get())->IsDestroyed()) { Unused << mContentParent->SendDestroySHEntrySharedState(mID); } SHEntrySharedParentState::Destroy(); } +LegacySHEntry::LegacySHEntry(PContentParent* aContentParent, + LegacySHistory* aSHistory, uint64_t aSharedID) + : nsSHEntry(new SHEntrySharedParent(aContentParent, aSHistory, aSharedID)), + mActor(nullptr) {} + NS_IMPL_ISUPPORTS_INHERITED0(LegacySHEntry, nsSHEntry) SHEntryParent* LegacySHEntry::CreateActor() { MOZ_ASSERT(!mActor); mActor = new SHEntryParent(this); return mActor; } @@ -39,22 +46,18 @@ MaybeNewPSHEntryParent LegacySHEntry::Ge return AsVariant(static_cast<PSHEntryParent*>(mActor)); } return AsVariant(NewPSHEntry{ aContentParent->OpenPSHEntryEndpoint(CreateActor()), mShared->mID}); } void LegacySHEntry::AbandonBFCacheEntry(uint64_t aNewSharedID) { - PContentParent* contentParent = - static_cast<SHEntrySharedParent*>(mShared.get())->GetContentParent(); - RefPtr<SHEntrySharedParent> shared = - new SHEntrySharedParent(contentParent, aNewSharedID); - shared->CopyFrom(mShared); - mShared = shared.forget(); + mShared = + static_cast<SHEntrySharedParent*>(mShared.get())->Duplicate(aNewSharedID); } NS_IMETHODIMP LegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) { *aBFCacheID = mShared->GetID(); return NS_OK; } @@ -296,17 +299,17 @@ bool SHEntryParent::RecvSetCsp(nsIConten return true; } bool SHEntryParent::RecvGetStateData(ClonedMessageData* aData) { nsCOMPtr<nsIStructuredCloneContainer> container = mEntry->GetStateData(); if (container) { static_cast<nsStructuredCloneContainer*>(container.get()) ->BuildClonedMessageDataForParent( - static_cast<ContentParent*>(Manager()), *aData); + static_cast<ContentParent*>(ToplevelProtocol()), *aData); } return true; } bool SHEntryParent::RecvSetStateData(ClonedMessageData&& aData) { // FIXME Need more data! Should we signal null separately from the // ClonedMessageData? if (aData.data().data.Size() == 0) {
--- a/docshell/shistory/SHEntryParent.h +++ b/docshell/shistory/SHEntryParent.h @@ -11,47 +11,60 @@ #include "mozilla/dom/MaybeNewPSHEntry.h" #include "mozilla/WeakPtr.h" #include "nsSHEntry.h" #include "nsSHEntryShared.h" namespace mozilla { namespace dom { +class LegacySHistory; class PContentParent; class SHEntryParent; /** * Implementation of the shared state for session history entries in the parent * process. */ class SHEntrySharedParent : public SHEntrySharedParentState { public: - SHEntrySharedParent(PContentParent* aContentParent, uint64_t aSharedID); + SHEntrySharedParent(PContentParent* aContentParent, LegacySHistory* aSHistory, + uint64_t aSharedID); + + already_AddRefed<SHEntrySharedParent> Duplicate(uint64_t aNewSharedID) { + RefPtr<SHEntrySharedParent> shared = + new SHEntrySharedParent(this, aNewSharedID); + shared->CopyFrom(this); + return shared.forget(); + } + + PContentParent* GetContentParent() { return mContentParent.get(); } + + protected: + SHEntrySharedParent(SHEntrySharedParent* aDuplicate, uint64_t aSharedID) + : SHEntrySharedParentState(aDuplicate, aSharedID), + mContentParent(aDuplicate->mContentParent) {} void Destroy() override; - PContentParent* GetContentParent() { return mContentParent.get(); } - private: mozilla::WeakPtr<PContentParent> mContentParent; }; /** * Session history entry implementation based on the legacy implementation that * used to live in the child process. Ideally this wouldn't implement nsISHEntry * (it should only ever be accessed by SHEntryParent and LegacySHistory). * The actor is (re)created as needed, whenever we need to return an entry to * the child process. The lifetime is determined by the child side. */ class LegacySHEntry final : public nsSHEntry, public CrossProcessSHEntry { public: - LegacySHEntry(PContentParent* aParent, uint64_t aSharedID) - : nsSHEntry(new SHEntrySharedParent(aParent, aSharedID)), - mActor(nullptr) {} + LegacySHEntry(PContentParent* aContentParent, LegacySHistory* aSHistory, + uint64_t aSharedID); explicit LegacySHEntry(const LegacySHEntry& aEntry) : nsSHEntry(aEntry), mActor(nullptr) {} NS_DECL_ISUPPORTS_INHERITED MaybeNewPSHEntryParent GetOrCreateActor(PContentParent* aContentParent); using nsSHEntry::AbandonBFCacheEntry; @@ -61,17 +74,17 @@ class LegacySHEntry final : public nsSHE uint64_t GetSharedStateID() const { return mShared->GetID(); } dom::SHEntrySharedParentState* GetSharedState() const { return mShared.get(); } private: friend class SHEntryParent; - friend class ContentParent; + friend class SHistoryParent; ~LegacySHEntry() {} SHEntryParent* CreateActor(); SHEntryParent* mActor; };
--- a/docshell/shistory/SHistoryChild.cpp +++ b/docshell/shistory/SHistoryChild.cpp @@ -190,17 +190,16 @@ SHistoryChild::AddEntry(nsISHEntry* aEnt nsresult rv; int32_t entriesPurged; if (!SendAddEntry(static_cast<SHEntryChild*>(aEntry), aPersist, &rv, &entriesPurged)) { return NS_ERROR_FAILURE; } NS_ENSURE_SUCCESS(rv, rv); - aEntry->SetSHistory(this); if (mRootDocShell) { aEntry->SetDocshellID(mRootDocShell->HistoryID()); if (entriesPurged > 0) { mRootDocShell->HistoryPurged(entriesPurged); } } @@ -219,17 +218,16 @@ 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); - aReplaceEntry->SetSHistory(this); return NS_OK; } NS_IMETHODIMP SHistoryChild::NotifyOnHistoryReload(bool* _retval) { return SendNotifyOnHistoryReload(_retval) ? NS_OK : NS_ERROR_FAILURE; } @@ -354,16 +352,28 @@ SHistoryChild::Reload(uint32_t aReloadFl 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(); }
--- a/docshell/shistory/SHistoryParent.cpp +++ b/docshell/shistory/SHistoryParent.cpp @@ -31,20 +31,36 @@ static void FillInLoadResult(PContentPar static_cast<LegacySHEntry*>(aLoadResult.mLoadState->SHEntry()), aLoadResult.mBrowsingContext, aLoadResult.mLoadState); } else { *aResult = aRv; } } SHistoryParent::SHistoryParent(CanonicalBrowsingContext* aContext) - : mContext(aContext), mHistory(new LegacySHistory(aContext, nsID())) {} + : mHistory(new LegacySHistory(aContext, nsID())) {} SHistoryParent::~SHistoryParent() {} +SHEntryParent* SHistoryParent::CreateEntry( + PContentParent* aContentParent, PSHistoryParent* aSHistoryParent, + const PSHEntryOrSharedID& aEntryOrSharedID) { + RefPtr<LegacySHEntry> entry; + if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) { + entry = new LegacySHEntry( + aContentParent, static_cast<SHistoryParent*>(aSHistoryParent)->mHistory, + aEntryOrSharedID.get_uint64_t()); + } else { + entry = new LegacySHEntry(*( + static_cast<const SHEntryParent*>(aEntryOrSharedID.get_PSHEntryParent()) + ->mEntry)); + } + return entry->CreateActor(); +} + void SHistoryParent::ActorDestroy(ActorDestroyReason aWhy) {} bool SHistoryParent::RecvGetCount(int32_t* aCount) { return NS_SUCCEEDED(mHistory->GetCount(aCount)); } bool SHistoryParent::RecvGetIndex(int32_t* aIndex) { return NS_SUCCEEDED(mHistory->GetIndex(aIndex)); @@ -135,16 +151,17 @@ bool SHistoryParent::RecvEvictOutOfRange } bool SHistoryParent::RecvEvictAllContentViewers() { return NS_SUCCEEDED(mHistory->EvictAllContentViewers()); } bool SHistoryParent::RecvEvictContentViewersOrReplaceEntry( PSHEntryParent* aNewSHEntry, bool aReplace) { + MOZ_ASSERT(Manager() == aNewSHEntry->Manager()); mHistory->EvictContentViewersOrReplaceEntry( aNewSHEntry ? static_cast<SHEntryParent*>(aNewSHEntry)->mEntry.get() : nullptr, aReplace); return true; } bool SHistoryParent::RecvRemoveDynEntries(int32_t aIndex, @@ -327,10 +344,21 @@ void LegacySHistory::EvictOutOfRangeWind for (auto iter = ids.ConstIter(); !iter.Done(); iter.Next()) { evictArray.AppendElement(iter.Get()->GetKey()); } Unused << parent->SendEvictContentViewers(evictArray); } } +NS_IMETHODIMP +LegacySHistory::CreateEntry(nsISHEntry** aEntry) { + NS_ENSURE_TRUE(mRootBC, NS_ERROR_FAILURE); + + NS_ADDREF(*aEntry = new LegacySHEntry( + static_cast<CanonicalBrowsingContext*>(mRootBC) + ->GetContentParent(), + this, SHEntryChildShared::CreateSharedID())); + return NS_OK; +} + } // namespace dom } // namespace mozilla
--- a/docshell/shistory/SHistoryParent.h +++ b/docshell/shistory/SHistoryParent.h @@ -2,54 +2,59 @@ /* 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/. */ #ifndef mozilla_dom_SHistoryParent_h #define mozilla_dom_SHistoryParent_h +#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/PSHistoryParent.h" #include "mozilla/RefPtr.h" #include "nsSHistory.h" namespace mozilla { namespace dom { -class CanonicalBrowsingContext; class SHistoryParent; /** * Session history implementation based on the legacy implementation that used * to live in the child process. Ideally this wouldn't implement nsISHistory * (it should only ever be accessed by SHistoryParent). */ class LegacySHistory final : public nsSHistory { private: virtual ~LegacySHistory() {} void EvictOutOfRangeWindowContentViewers(int32_t aIndex) override; public: - LegacySHistory(mozilla::dom::CanonicalBrowsingContext* aRootBC, - const nsID& aDocShellID); + LegacySHistory(CanonicalBrowsingContext* aRootBC, const nsID& aDocShellID); + + NS_IMETHOD CreateEntry(nsISHEntry** aEntry) override; }; /** * Session history actor for the parent process. Forwards to the legacy * implementation that used to live in the child process (see LegacySHistory). */ class SHistoryParent final : public PSHistoryParent { friend class PSHistoryParent; friend class SHEntryParent; public: explicit SHistoryParent(CanonicalBrowsingContext* aContext); virtual ~SHistoryParent(); + static SHEntryParent* CreateEntry(PContentParent* aContentParent, + PSHistoryParent* aSHistoryParent, + const PSHEntryOrSharedID& aEntryOrSharedID); + protected: void ActorDestroy(ActorDestroyReason aWhy) override; private: bool RecvGetCount(int32_t* aCount); bool RecvGetIndex(int32_t* aIndex); bool RecvSetIndex(int32_t aIndex, nsresult* aResult); bool RecvGetRequestedIndex(int32_t* aIndex); @@ -79,16 +84,15 @@ class SHistoryParent final : public PSHi RefPtr<CrossProcessSHEntry>* aEntry, int32_t* aIndex); 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; }; } // namespace dom } // namespace mozilla #endif /* mozilla_dom_SHistoryParent_h */
--- a/docshell/shistory/nsISHEntry.idl +++ b/docshell/shistory/nsISHEntry.idl @@ -213,21 +213,19 @@ interface nsISHEntry : nsISupports /** * Flag to indicate that the history entry was originally loaded in the * current process. This flag does not survive a browser process switch. */ [infallible] readonly attribute boolean loadedInThisProcess; /** - * The session history it belongs to. It's usually only set on root entries. - * SHEntry is strictly bound to the SHistory it belongs to; it should not be - * changed once set to a non-null value. + * The session history it belongs to. */ - [noscript, infallible] attribute nsISHistory SHistory; + [infallible] readonly attribute nsISHistory shistory; /** * A number that is assigned by the sHistory when the entry is activated */ [noscript, infallible] attribute unsigned long lastTouched; /** * The current number of nsISHEntries which are immediate children of this
--- a/docshell/shistory/nsISHistory.idl +++ b/docshell/shistory/nsISHistory.idl @@ -259,9 +259,11 @@ interface nsISHistory: nsISupports void RemoveFrameEntries(in nsISHEntry aEntry); [noscript] void Reload(in unsigned long aReloadFlags); [notxpcom] void EnsureCorrectEntryAtCurrIndex(in nsISHEntry aEntry); [notxpcom] void EvictContentViewersOrReplaceEntry(in nsISHEntry aNewSHEntry, in bool aReplace); + + nsISHEntry createEntry(); };
--- a/docshell/shistory/nsSHEntry.cpp +++ b/docshell/shistory/nsSHEntry.cpp @@ -915,32 +915,23 @@ nsSHEntry::GetLastTouched(uint32_t* aLas NS_IMETHODIMP nsSHEntry::SetLastTouched(uint32_t aLastTouched) { mShared->mLastTouched = aLastTouched; return NS_OK; } NS_IMETHODIMP -nsSHEntry::GetSHistory(nsISHistory** aSHistory) { +nsSHEntry::GetShistory(nsISHistory** aSHistory) { nsCOMPtr<nsISHistory> shistory(do_QueryReferent(mShared->mSHistory)); shistory.forget(aSHistory); return NS_OK; } NS_IMETHODIMP -nsSHEntry::SetSHistory(nsISHistory* aSHistory) { - nsWeakPtr shistory = do_GetWeakReference(aSHistory); - // mSHistory can not be changed once it's set - MOZ_ASSERT(!mShared->mSHistory || (mShared->mSHistory == shistory)); - mShared->mSHistory = shistory; - return NS_OK; -} - -NS_IMETHODIMP nsSHEntry::SetLoadTypeAsHistory() { // Set the LoadType by default to loadHistory during creation mLoadType = LOAD_HISTORY; return NS_OK; } NS_IMETHODIMP nsSHEntry::GetPersist(bool* aPersist) { @@ -1042,18 +1033,18 @@ void nsSHEntry::EvictContentViewer() { // Drop the presentation state before destroying the viewer, so that // document teardown is able to correctly persist the state. SetContentViewer(nullptr); SyncPresentationState(); viewer->Destroy(); } } -nsLegacySHEntry::nsLegacySHEntry(uint64_t aID) - : nsSHEntry(new nsSHEntryShared(aID)) {} +nsLegacySHEntry::nsLegacySHEntry(nsSHistory* aHistory, uint64_t aID) + : nsSHEntry(new nsSHEntryShared(aHistory, aID)) {} NS_IMETHODIMP nsLegacySHEntry::SetContentViewer(nsIContentViewer* aViewer) { return GetState()->SetContentViewer(aViewer); } NS_IMETHODIMP nsLegacySHEntry::GetContentViewer(nsIContentViewer** aResult) { @@ -1200,18 +1191,18 @@ bool nsLegacySHEntry::HasDetachedEditor( } bool nsLegacySHEntry::HasBFCacheEntry(nsIBFCacheEntry* aEntry) { return static_cast<nsIBFCacheEntry*>(GetState()) == aEntry; } NS_IMETHODIMP nsLegacySHEntry::AbandonBFCacheEntry() { - mShared = nsSHEntryShared::Duplicate( - GetState(), mozilla::dom::SHEntryChildShared::CreateSharedID()); + mShared = + GetState()->Duplicate(mozilla::dom::SHEntryChildShared::CreateSharedID()); return NS_OK; } NS_IMETHODIMP nsLegacySHEntry::GetBfcacheID(uint64_t* aBFCacheID) { *aBFCacheID = mShared->GetID(); return NS_OK; }
--- a/docshell/shistory/nsSHEntry.h +++ b/docshell/shistory/nsSHEntry.h @@ -74,17 +74,17 @@ class nsSHEntry : public nsISHEntry { /** * Session history entry class used for implementing session history for * docshells in the parent process (a different solution would be to use the * IPC actors for that too, with both parent and child actor created in the * parent process). */ class nsLegacySHEntry final : public nsSHEntry { public: - explicit nsLegacySHEntry(uint64_t aID); + explicit nsLegacySHEntry(nsSHistory* aHistory, uint64_t aID); explicit nsLegacySHEntry(const nsLegacySHEntry& aOther) : nsSHEntry(aOther) {} NS_IMETHOD GetContentViewer(nsIContentViewer** aResult) override; NS_IMETHOD SetContentViewer(nsIContentViewer* aViewer) override; NS_IMETHOD GetWindowState(nsISupports** aState) override; NS_IMETHOD SetWindowState(nsISupports* aState) override; using nsISHEntry::GetRefreshURIList; NS_IMETHOD GetRefreshURIList(nsIMutableArray** aRefreshURIList) override;
--- a/docshell/shistory/nsSHEntryShared.cpp +++ b/docshell/shistory/nsSHEntryShared.cpp @@ -8,42 +8,49 @@ #include "nsArray.h" #include "nsDocShellEditorData.h" #include "nsIContentViewer.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "mozilla/dom/Document.h" #include "nsILayoutHistoryState.h" -#include "nsISHistory.h" #include "nsIWebNavigation.h" +#include "nsSHistory.h" #include "nsThreadUtils.h" #include "mozilla/Attributes.h" #include "mozilla/Preferences.h" namespace dom = mozilla::dom; -void nsSHEntryShared::Shutdown() {} +namespace mozilla { +namespace dom { -dom::SHEntrySharedParentState::SHEntrySharedParentState(uint64_t aID) +SHEntrySharedParentState::SHEntrySharedParentState(nsSHistory* aSHistory, + uint64_t aID) + : SHEntrySharedParentState(nsWeakPtr(do_GetWeakReference(aSHistory)).get(), + aID) {} + +SHEntrySharedParentState::SHEntrySharedParentState(nsIWeakReference* aSHistory, + uint64_t aID) : mDocShellID({0}), mViewerBounds(0, 0, 0, 0), mCacheKey(0), mLastTouched(0), mID(aID), + mSHistory(aSHistory), mIsFrameNavigation(false), mSticky(true), mDynamicallyCreated(false), mExpired(false) {} -dom::SHEntrySharedParentState::~SHEntrySharedParentState() {} +SHEntrySharedParentState::~SHEntrySharedParentState() {} -void dom::SHEntrySharedParentState::CopyFrom( - dom::SHEntrySharedParentState* aEntry) { +void SHEntrySharedParentState::CopyFrom(SHEntrySharedParentState* aEntry) { mDocShellID = aEntry->mDocShellID; mTriggeringPrincipal = aEntry->mTriggeringPrincipal; mPrincipalToInherit = aEntry->mPrincipalToInherit; mStoragePrincipalToInherit = aEntry->mStoragePrincipalToInherit; mCsp = aEntry->mCsp; mContentType.Assign(aEntry->mContentType); mIsFrameNavigation = aEntry->mIsFrameNavigation; mSticky = aEntry->mSticky; @@ -57,24 +64,25 @@ void dom::SHEntrySharedParentState::Noti RefPtr<nsSHistory> nsshistory = static_cast<nsSHistory*>(shistory.get()); nsshistory->NotifyListenersContentViewerEvicted(1); } } dom::SHEntrySharedChildState::SHEntrySharedChildState() : mSaveLayoutState(true) {} -void dom::SHEntrySharedChildState::CopyFrom( - dom::SHEntrySharedChildState* aEntry) { +void SHEntrySharedChildState::CopyFrom(SHEntrySharedChildState* aEntry) { mChildShells.AppendObjects(aEntry->mChildShells); mSaveLayoutState = aEntry->mSaveLayoutState; } -nsSHEntryShared::nsSHEntryShared(uint64_t aID) - : dom::SHEntrySharedParentState(aID) {} +} // namespace dom +} // namespace mozilla + +void nsSHEntryShared::Shutdown() {} nsSHEntryShared::~nsSHEntryShared() { // The destruction can be caused by either the entry is removed from session // history and no one holds the reference, or the whole session history is on // destruction. We want to ensure that we invoke // shistory->RemoveFromExpirationTracker for the former case. RemoveFromExpirationTracker(); @@ -89,21 +97,21 @@ nsSHEntryShared::~nsSHEntryShared() { } } NS_IMPL_QUERY_INTERFACE(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver) NS_IMPL_ADDREF_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState) NS_IMPL_RELEASE_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState) already_AddRefed<nsSHEntryShared> nsSHEntryShared::Duplicate( - nsSHEntryShared* aEntry, uint64_t aNewSharedID) { - RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(aNewSharedID); + uint64_t aNewSharedID) { + RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(this, aNewSharedID); - newEntry->dom::SHEntrySharedParentState::CopyFrom(aEntry); - newEntry->dom::SHEntrySharedChildState::CopyFrom(aEntry); + newEntry->dom::SHEntrySharedParentState::CopyFrom(this); + newEntry->dom::SHEntrySharedChildState::CopyFrom(this); return newEntry.forget(); } void nsSHEntryShared::RemoveFromExpirationTracker() { nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory); if (shistory && GetExpirationState()->IsTracked()) { shistory->RemoveFromExpirationTracker(this);
--- a/docshell/shistory/nsSHEntryShared.h +++ b/docshell/shistory/nsSHEntryShared.h @@ -21,16 +21,17 @@ class nsSHEntry; class nsISHEntry; class nsIContentViewer; class nsIDocShellTreeItem; class nsILayoutHistoryState; class nsDocShellEditorData; class nsIMutableArray; +class nsSHistory; // A document may have multiple SHEntries, either due to hash navigations or // calls to history.pushState. SHEntries corresponding to the same document // share many members; in particular, they share state related to the // back/forward cache. // // The classes defined here are the vehicle for this sharing. // @@ -42,25 +43,25 @@ namespace dom { class Document; /** * SHEntrySharedParentState holds the shared state that can live in the parent * process. */ class SHEntrySharedParentState { public: - explicit SHEntrySharedParentState(uint64_t aID); - uint64_t GetID() const { return mID; } void NotifyListenersContentViewerEvicted(); protected: - friend class nsSHEntry; - + SHEntrySharedParentState(nsSHistory* aSHistory, uint64_t aID); + SHEntrySharedParentState(SHEntrySharedParentState* aDuplicate, uint64_t aID) + : SHEntrySharedParentState(aDuplicate->mSHistory, aID) {} + SHEntrySharedParentState(nsIWeakReference* aDuplicate, uint64_t aID); virtual ~SHEntrySharedParentState(); NS_INLINE_DECL_VIRTUAL_REFCOUNTING_WITH_DESTROY(SHEntrySharedParentState, Destroy()) virtual void Destroy() { delete this; } void CopyFrom(SHEntrySharedParentState* aSource); @@ -132,33 +133,32 @@ class SHEntrySharedChildState { class nsSHEntryShared final : public nsIBFCacheEntry, public nsStubMutationObserver, public mozilla::dom::SHEntrySharedParentState, public mozilla::dom::SHEntrySharedChildState { public: static void EnsureHistoryTracker(); static void Shutdown(); - explicit nsSHEntryShared(uint64_t aID); + using SHEntrySharedParentState::SHEntrySharedParentState; + + already_AddRefed<nsSHEntryShared> Duplicate(uint64_t aNewSharedID); NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIBFCACHEENTRY // The nsIMutationObserver bits we actually care about. NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED nsExpirationState* GetExpirationState() { return &mExpirationState; } - static already_AddRefed<nsSHEntryShared> Duplicate(nsSHEntryShared* aEntry, - uint64_t aNewSharedID); - private: ~nsSHEntryShared(); friend class nsLegacySHEntry; void RemoveFromExpirationTracker(); void SyncPresentationState(); void DropPresentationState();
--- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -189,20 +189,20 @@ void nsSHistory::EvictContentViewerForEn // well. int32_t index = GetIndexOfEntry(aEntry); if (index != -1) { RemoveDynEntries(index, aEntry); } } nsSHistory::nsSHistory(BrowsingContext* aRootBC, const nsID& aRootDocShellID) - : mIsRemote(false), + : mRootBC(aRootBC), + mIsRemote(false), mIndex(-1), mRequestedIndex(-1), - mRootBC(aRootBC), mRootDocShellID(aRootDocShellID) { // Add this new SHistory object to the list gSHistoryList.insertBack(this); // Init mHistoryTracker on setting mRootBC so we can bind its event // target to the tabGroup. nsPIDOMWindowOuter* win; if (mRootBC && (win = mRootBC->GetDOMWindow())) { @@ -548,27 +548,25 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntr int32_t dummy = 0; return AddEntry(aSHEntry, aPersist, &dummy); } nsresult nsSHistory::AddEntry(nsISHEntry* aSHEntry, bool aPersist, int32_t* aEntriesPurged) { NS_ENSURE_ARG(aSHEntry); - nsCOMPtr<nsISHistory> shistoryOfEntry = aSHEntry->GetSHistory(); - if (shistoryOfEntry && shistoryOfEntry != this) { + nsCOMPtr<nsISHistory> shistoryOfEntry = aSHEntry->GetShistory(); + if (shistoryOfEntry != this) { NS_WARNING( "The entry has been associated to another nsISHistory instance. " "Try nsISHEntry.clone() and nsISHEntry.abandonBFCacheEntry() " "first if you're copying an entry from another nsISHistory."); return NS_ERROR_FAILURE; } - aSHEntry->SetSHistory(this); - // If we have a root docshell, update the docshell id of the root shentry to // match the id of that docshell if (mRootBC) { aSHEntry->SetDocshellID(mRootDocShellID); } if (mIndex >= 0) { MOZ_ASSERT(mIndex < Length(), "Index out of range!"); @@ -765,27 +763,25 @@ nsSHistory::RemoveSHistoryListener(nsISH NS_IMETHODIMP nsSHistory::ReplaceEntry(int32_t aIndex, nsISHEntry* aReplaceEntry) { NS_ENSURE_ARG(aReplaceEntry); if (aIndex < 0 || aIndex >= Length()) { return NS_ERROR_FAILURE; } - nsCOMPtr<nsISHistory> shistoryOfEntry = aReplaceEntry->GetSHistory(); - if (shistoryOfEntry && shistoryOfEntry != this) { + nsCOMPtr<nsISHistory> shistoryOfEntry = aReplaceEntry->GetShistory(); + if (shistoryOfEntry != this) { NS_WARNING( "The entry has been associated to another nsISHistory instance. " "Try nsISHEntry.clone() and nsISHEntry.abandonBFCacheEntry() " "first if you're copying an entry from another nsISHistory."); return NS_ERROR_FAILURE; } - aReplaceEntry->SetSHistory(this); - NOTIFY_LISTENERS(OnHistoryReplaceEntry, ()); aReplaceEntry->SetPersist(true); mEntries[aIndex] = aReplaceEntry; return NS_OK; } @@ -1573,8 +1569,16 @@ nsresult nsSHistory::InitiateLoad(nsISHE loadState->SetFirstParty(false); nsCOMPtr<nsIContentSecurityPolicy> csp = aFrameEntry->GetCsp(); loadState->SetCsp(csp); aLoadResult.mLoadState = loadState.forget(); return NS_OK; } + +NS_IMETHODIMP +nsSHistory::CreateEntry(nsISHEntry** aEntry) { + nsCOMPtr<nsISHEntry> entry = + new nsLegacySHEntry(this, SHEntryChildShared::CreateSharedID()); + entry.forget(aEntry); + return NS_OK; +}
--- a/docshell/shistory/nsSHistory.h +++ b/docshell/shistory/nsSHistory.h @@ -143,16 +143,19 @@ class nsSHistory : public mozilla::Linke void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex, int32_t* aOutEndIndex); void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted); protected: virtual ~nsSHistory(); + // Weak reference. Do not refcount this. + mozilla::dom::BrowsingContext* mRootBC; + private: friend class nsSHistoryObserver; nsresult LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry, mozilla::dom::BrowsingContext* aRootBC, long aLoadType, bool& aDifferenceFound, LoadEntryResult& aLoadResult); nsresult InitiateLoad(nsISHEntry* aFrameEntry, @@ -202,18 +205,16 @@ class nsSHistory : public mozilla::Linke mozilla::UniquePtr<HistoryTracker> mHistoryTracker; int32_t mIndex; // -1 means "no index" int32_t mRequestedIndex; // -1 means "no requested index" // Session History listeners nsAutoTObserverArray<nsWeakPtr, 2> mListeners; - // Weak reference. Do not refcount this. - mozilla::dom::BrowsingContext* mRootBC; nsID mRootDocShellID; // Max viewers allowed total, across all SHistory objects static int32_t sHistoryMaxTotalViewers; }; inline nsISupports* ToSupports(nsSHistory* aObj) { return static_cast<nsISHistory*>(aObj);
--- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3474,17 +3474,17 @@ bool ContentChild::DeallocPSessionStorag PSessionStorageObserverChild* aActor) { MOZ_ASSERT(aActor); delete aActor; return true; } PSHEntryChild* ContentChild::AllocPSHEntryChild( - const PSHEntryOrSharedID& aEntryOrSharedID) { + PSHistoryChild* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID) { // We take a strong reference for the IPC layer. The Release implementation // for SHEntryChild will ask the IPC layer to release it (through // DeallocPSHEntryChild) if that is the only remaining reference. RefPtr<SHEntryChild> child; if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) { child = new SHEntryChild(aEntryOrSharedID.get_uint64_t()); } else { child = new SHEntryChild(
--- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -619,17 +619,18 @@ class ContentChild final : public PConte bool DeallocPLoginReputationChild(PLoginReputationChild* aActor); PSessionStorageObserverChild* AllocPSessionStorageObserverChild(); bool DeallocPSessionStorageObserverChild( PSessionStorageObserverChild* aActor); - PSHEntryChild* AllocPSHEntryChild(const PSHEntryOrSharedID& aEntryOrSharedID); + PSHEntryChild* AllocPSHEntryChild(PSHistoryChild* aSHistory, + const PSHEntryOrSharedID& aEntryOrSharedID); void DeallocPSHEntryChild(PSHEntryChild*); PSHistoryChild* AllocPSHistoryChild(BrowsingContext* aContext); void DeallocPSHistoryChild(PSHistoryChild* aActor); nsTArray<LookAndFeelInt>& LookAndFeelCache() { return mLookAndFeelCache; } @@ -686,17 +687,18 @@ class ContentChild final : public PConte } uint64_t NextBrowsingContextFieldEpoch() { mBrowsingContextFieldEpoch++; return mBrowsingContextFieldEpoch; } mozilla::ipc::IPCResult RecvDestroySHEntrySharedState(const uint64_t& aID); - mozilla::ipc::IPCResult RecvEvictContentViewers(nsTArray<uint64_t>&& aToEvictSharedStateIDs); + mozilla::ipc::IPCResult RecvEvictContentViewers( + nsTArray<uint64_t>&& aToEvictSharedStateIDs); #ifdef NIGHTLY_BUILD // Fetch the current number of pending input events. // // NOTE: This method performs an atomic read, and is safe to call from all // threads. uint32_t GetPendingInputEvents() { return mPendingInputEvents; } #endif
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5697,26 +5697,18 @@ bool ContentParent::DeallocPSessionStora PSessionStorageObserverParent* aActor) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aActor); return mozilla::dom::DeallocPSessionStorageObserverParent(aActor); } PSHEntryParent* ContentParent::AllocPSHEntryParent( - const PSHEntryOrSharedID& aEntryOrSharedID) { - RefPtr<LegacySHEntry> entry; - if (aEntryOrSharedID.type() == PSHEntryOrSharedID::Tuint64_t) { - entry = new LegacySHEntry(this, aEntryOrSharedID.get_uint64_t()); - } else { - entry = new LegacySHEntry(*( - static_cast<const SHEntryParent*>(aEntryOrSharedID.get_PSHEntryParent()) - ->mEntry)); - } - return entry->CreateActor(); + PSHistoryParent* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID) { + return SHistoryParent::CreateEntry(this, aSHistory, aEntryOrSharedID); } void ContentParent::DeallocPSHEntryParent(PSHEntryParent* aEntry) { delete static_cast<SHEntryParent*>(aEntry); } PSHistoryParent* ContentParent::AllocPSHistoryParent( BrowsingContext* aContext) {
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -578,17 +578,17 @@ class ContentParent final : public PCont virtual mozilla::ipc::IPCResult RecvPSessionStorageObserverConstructor( PSessionStorageObserverParent* aActor) override; bool DeallocPSessionStorageObserverParent( PSessionStorageObserverParent* aActor); PSHEntryParent* AllocPSHEntryParent( - const PSHEntryOrSharedID& aEntryOrSharedID); + PSHistoryParent* aSHistory, const PSHEntryOrSharedID& aEntryOrSharedID); void DeallocPSHEntryParent(PSHEntryParent*); PSHistoryParent* AllocPSHistoryParent(BrowsingContext* aContext); void DeallocPSHistoryParent(PSHistoryParent* aActor); bool DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor);
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -923,17 +923,17 @@ parent: async PLoginReputation(URIParams formURI); async PSessionStorageObserver(); async PSHistory(BrowsingContext aContext); // Clone from entry or use shared id. - sync PSHEntry(PSHEntryOrSharedID entryOrSharedID); + sync PSHEntry(PSHistory shistory, PSHEntryOrSharedID entryOrSharedID); async PBenchmarkStorage(); // Services remoting async StartVisitedQuery(URIParams uri); async SetURITitle(URIParams uri, nsString title);
--- a/toolkit/components/viewsource/content/viewSource-content.js +++ b/toolkit/components/viewsource/content/viewSource-content.js @@ -270,28 +270,26 @@ var ViewSourceContent = { ); } catch (e) { // We were not able to load the source from the network cache. this.loadSourceFromURL(viewSrcURL); return; } let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry); - let shEntry = Cc[ - "@mozilla.org/browser/session-history-entry;1" - ].createInstance(Ci.nsISHEntry); + let shistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory + .legacySHistory; + let shEntry = shistory.createEntry(); shEntry.URI = Services.io.newURI(viewSrcURL); shEntry.title = viewSrcURL; let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); shEntry.triggeringPrincipal = systemPrincipal; shEntry.setLoadTypeAsHistory(); shEntry.cacheKey = shEntrySource.cacheKey; - docShell - .QueryInterface(Ci.nsIWebNavigation) - .sessionHistory.legacySHistory.addEntry(shEntry, true); + shistory.addEntry(shEntry, true); }, /** * Load some URL in the browser. * * @param URL * The URL string to load. */
--- a/toolkit/modules/sessionstore/SessionHistory.jsm +++ b/toolkit/modules/sessionstore/SessionHistory.jsm @@ -346,17 +346,17 @@ var SessionHistoryInternal = { for (let i = 0; i < tabData.entries.length; i++) { let entry = tabData.entries[i]; // XXXzpao Wallpaper patch for bug 514751 if (!entry.url) { continue; } let persist = "persist" in entry ? entry.persist : true; history.addEntry( - this.deserializeEntry(entry, idMap, docIdentMap), + this.deserializeEntry(entry, idMap, docIdentMap, history), persist ); } // Select the right history entry. let index = tabData.index - 1; if (index < history.count && history.index != index) { history.index = index; @@ -370,20 +370,18 @@ var SessionHistoryInternal = { * @param entry * Object containing serialized history data for a URL * @param idMap * Hash for ensuring unique frame IDs * @param docIdentMap * Hash to ensure reuse of BFCache entries * @returns nsISHEntry */ - deserializeEntry(entry, idMap, docIdentMap) { - var shEntry = Cc[ - "@mozilla.org/browser/session-history-entry;1" - ].createInstance(Ci.nsISHEntry); + deserializeEntry(entry, idMap, docIdentMap, shistory) { + var shEntry = shistory.createEntry(); shEntry.URI = Services.io.newURI(entry.url); shEntry.title = entry.title || entry.url; if (entry.subframe) { shEntry.isSubFrame = entry.subframe || false; } shEntry.setLoadTypeAsHistory(); if (entry.contentType) { @@ -558,17 +556,22 @@ var SessionHistoryInternal = { // // So as a hack to fix this, we restrict the scope of a doc identifier // to be a node's siblings and cousins, and pass childDocIdents, not // aDocIdents, to _deserializeHistoryEntry. That is, we say that two // SHEntries with the same doc identifier have the same document iff // they have the same parent or their parents have the same document. shEntry.AddChild( - this.deserializeEntry(entry.children[i], idMap, childDocIdents), + this.deserializeEntry( + entry.children[i], + idMap, + childDocIdents, + shEntry.shistory + ), i ); } } return shEntry; },