Backed out changeset ebc60855035e (bug 1829026) as requested for causing Bug 1869760...
[gecko.git] / docshell / shistory / nsSHistory.h
blob97bbe7f0a92167a53c5dd64320fb6369540ecf07
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsSHistory_h
8 #define nsSHistory_h
10 #include "nsCOMPtr.h"
11 #include "nsDocShellLoadState.h"
12 #include "nsExpirationTracker.h"
13 #include "nsISHistory.h"
14 #include "nsSHEntryShared.h"
15 #include "nsSimpleEnumerator.h"
16 #include "nsTObserverArray.h"
17 #include "nsWeakReference.h"
19 #include "mozilla/dom/ipc/IdType.h"
20 #include "mozilla/LinkedList.h"
21 #include "mozilla/UniquePtr.h"
23 class nsIDocShell;
24 class nsDocShell;
25 class nsSHistoryObserver;
26 class nsISHEntry;
28 namespace mozilla {
29 namespace dom {
30 class LoadSHEntryResult;
32 } // namespace mozilla
34 class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
35 public nsISHistory,
36 public nsSupportsWeakReference {
37 public:
38 // The timer based history tracker is used to evict bfcache on expiration.
39 class HistoryTracker final
40 : public nsExpirationTracker<mozilla::dom::SHEntrySharedParentState, 3> {
41 public:
42 explicit HistoryTracker(nsSHistory* aSHistory, uint32_t aTimeout,
43 nsIEventTarget* aEventTarget)
44 : nsExpirationTracker(1000 * aTimeout / 2, "HistoryTracker",
45 aEventTarget) {
46 MOZ_ASSERT(aSHistory);
47 mSHistory = aSHistory;
50 protected:
51 virtual void NotifyExpired(
52 mozilla::dom::SHEntrySharedParentState* aObj) override {
53 RemoveObject(aObj);
54 mSHistory->EvictExpiredDocumentViewerForEntry(aObj);
57 private:
58 // HistoryTracker is owned by nsSHistory; it always outlives HistoryTracker
59 // so it's safe to use raw pointer here.
60 nsSHistory* mSHistory;
63 // Structure used in SetChildHistoryEntry
64 struct SwapEntriesData {
65 mozilla::dom::BrowsingContext*
66 ignoreBC; // constant; the browsing context to ignore
67 nsISHEntry* destTreeRoot; // constant; the root of the dest tree
68 nsISHEntry* destTreeParent; // constant; the node under destTreeRoot
69 // whose children will correspond to aEntry
72 explicit nsSHistory(mozilla::dom::BrowsingContext* aRootBC);
73 NS_DECL_ISUPPORTS
74 NS_DECL_NSISHISTORY
76 // One time initialization method
77 static nsresult Startup();
78 static void Shutdown();
79 static void UpdatePrefs();
81 // Max number of total cached content viewers. If the pref
82 // browser.sessionhistory.max_total_viewers is negative, then
83 // this value is calculated based on the total amount of memory.
84 // Otherwise, it comes straight from the pref.
85 static uint32_t GetMaxTotalViewers() { return sHistoryMaxTotalViewers; }
87 // Get the root SHEntry from a given entry.
88 static already_AddRefed<nsISHEntry> GetRootSHEntry(nsISHEntry* aEntry);
90 // Callback prototype for WalkHistoryEntries.
91 // `aEntry` is the child history entry, `aBC` is its corresponding browsing
92 // context, `aChildIndex` is the child's index in its parent entry, and
93 // `aData` is the opaque pointer passed to WalkHistoryEntries. Both structs
94 // that are passed as `aData` to this function have a field
95 // `aEntriesToUpdate`, which is an array of entries we need to update in
96 // docshell, if the 'SH in parent' pref is on (which implies that this method
97 // is executed in the parent)
98 typedef nsresult (*WalkHistoryEntriesFunc)(nsISHEntry* aEntry,
99 mozilla::dom::BrowsingContext* aBC,
100 int32_t aChildIndex, void* aData);
102 // Clone a session history tree for subframe navigation.
103 // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
104 // for the entry with id |aCloneID|, which will be replaced with
105 // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
106 // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
107 // have that pointer updated to point to the cloned history entry.
108 // If aCloneChildren is true then the children of the entry with id
109 // |aCloneID| will be cloned into |aReplaceEntry|.
110 static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
111 mozilla::dom::BrowsingContext* aOwnerBC,
112 uint32_t aCloneID, nsISHEntry* aReplaceEntry,
113 bool aCloneChildren, nsISHEntry** aDestEntry);
115 // Child-walking callback for CloneAndReplace
116 static nsresult CloneAndReplaceChild(nsISHEntry* aEntry,
117 mozilla::dom::BrowsingContext* aOwnerBC,
118 int32_t aChildIndex, void* aData);
120 // Child-walking callback for SetHistoryEntry
121 static nsresult SetChildHistoryEntry(nsISHEntry* aEntry,
122 mozilla::dom::BrowsingContext* aBC,
123 int32_t aEntryIndex, void* aData);
125 // For each child of aRootEntry, find the corresponding shell which is
126 // a child of aBC, and call aCallback. The opaque pointer aData
127 // is passed to the callback.
128 static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
129 mozilla::dom::BrowsingContext* aBC,
130 WalkHistoryEntriesFunc aCallback,
131 void* aData);
133 // This function finds all entries that are contiguous and same-origin with
134 // the aEntry. And call the aCallback on them, including the aEntry. This only
135 // works for the root entries. It will do nothing for non-root entries.
136 static void WalkContiguousEntries(
137 nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback);
139 nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; }
141 void NotifyOnHistoryReplaceEntry();
143 void RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex,
144 bool* aDidRemove);
146 // The size of the window of SHEntries which can have alive viewers in the
147 // bfcache around the currently active SHEntry.
149 // We try to keep viewers for SHEntries between index - VIEWER_WINDOW and
150 // index + VIEWER_WINDOW alive.
151 static const int32_t VIEWER_WINDOW = 3;
153 struct LoadEntryResult {
154 RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
155 RefPtr<nsDocShellLoadState> mLoadState;
158 static void LoadURIs(nsTArray<LoadEntryResult>& aLoadResults);
159 static void LoadURIOrBFCache(LoadEntryResult& aLoadEntry);
161 // If this doesn't return an error then either aLoadResult is set to nothing,
162 // in which case the caller should ignore the load, or it returns a valid
163 // LoadEntryResult in aLoadResult which the caller should use to do the load.
164 nsresult Reload(uint32_t aReloadFlags,
165 nsTArray<LoadEntryResult>& aLoadResults);
166 nsresult ReloadCurrentEntry(nsTArray<LoadEntryResult>& aLoadResults);
167 nsresult GotoIndex(int32_t aIndex, nsTArray<LoadEntryResult>& aLoadResults,
168 bool aSameEpoch, bool aLoadCurrentEntry,
169 bool aUserActivation);
171 void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex,
172 int32_t* aOutEndIndex);
173 void NotifyListenersDocumentViewerEvicted(uint32_t aNumEvicted);
175 int32_t Length() { return int32_t(mEntries.Length()); }
176 int32_t Index() { return mIndex; }
177 already_AddRefed<mozilla::dom::BrowsingContext> GetBrowsingContext() {
178 return mozilla::dom::BrowsingContext::Get(mRootBC);
180 bool HasOngoingUpdate() { return mHasOngoingUpdate; }
181 void SetHasOngoingUpdate(bool aVal) { mHasOngoingUpdate = aVal; }
183 void SetBrowsingContext(mozilla::dom::BrowsingContext* aRootBC) {
184 uint64_t newID = aRootBC ? aRootBC->Id() : 0;
185 if (mRootBC != newID) {
186 mRootBC = newID;
187 UpdateRootBrowsingContextState(aRootBC);
191 int32_t GetIndexForReplace() {
192 // Replace current entry in session history; If the requested index is
193 // valid, it indicates the loading was triggered by a history load, and
194 // we should replace the entry at requested index instead.
195 return mRequestedIndex == -1 ? mIndex : mRequestedIndex;
198 // Update the root browsing context state when adding, removing or
199 // replacing entries.
200 void UpdateRootBrowsingContextState() {
201 RefPtr<mozilla::dom::BrowsingContext> rootBC(GetBrowsingContext());
202 UpdateRootBrowsingContextState(rootBC);
205 void GetEpoch(uint64_t& aEpoch,
206 mozilla::Maybe<mozilla::dom::ContentParentId>& aId) const {
207 aEpoch = mEpoch;
208 aId = mEpochParentId;
210 void SetEpoch(uint64_t aEpoch,
211 mozilla::Maybe<mozilla::dom::ContentParentId> aId) {
212 mEpoch = aEpoch;
213 mEpochParentId = aId;
216 void LogHistory();
218 protected:
219 virtual ~nsSHistory();
221 uint64_t mRootBC;
223 private:
224 friend class nsSHistoryObserver;
226 void UpdateRootBrowsingContextState(
227 mozilla::dom::BrowsingContext* aBrowsingContext);
229 bool LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry,
230 mozilla::dom::BrowsingContext* aParent,
231 long aLoadType,
232 nsTArray<LoadEntryResult>& aLoadResults,
233 bool aLoadCurrentEntry, bool aUserActivation,
234 int32_t aOffset);
235 void InitiateLoad(nsISHEntry* aFrameEntry,
236 mozilla::dom::BrowsingContext* aFrameBC, long aLoadType,
237 nsTArray<LoadEntryResult>& aLoadResult,
238 bool aLoadCurrentEntry, bool aUserActivation,
239 int32_t aOffset);
241 nsresult LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd,
242 nsTArray<LoadEntryResult>& aLoadResults, bool aSameEpoch,
243 bool aLoadCurrentEntry, bool aUserActivation);
245 // Find the history entry for a given bfcache entry. It only looks up between
246 // the range where alive viewers may exist (i.e nsSHistory::VIEWER_WINDOW).
247 nsresult FindEntryForBFCache(mozilla::dom::SHEntrySharedParentState* aEntry,
248 nsISHEntry** aResult, int32_t* aResultIndex);
250 // Evict content viewers in this window which don't lie in the "safe" range
251 // around aIndex.
252 virtual void EvictOutOfRangeWindowDocumentViewers(int32_t aIndex);
253 void EvictDocumentViewerForEntry(nsISHEntry* aEntry);
254 static void GloballyEvictDocumentViewers();
255 static void GloballyEvictAllDocumentViewers();
257 // Calculates a max number of total
258 // content viewers to cache, based on amount of total memory
259 static uint32_t CalcMaxTotalViewers();
261 nsresult LoadNextPossibleEntry(int32_t aNewIndex, long aLoadType,
262 uint32_t aHistCmd,
263 nsTArray<LoadEntryResult>& aLoadResults,
264 bool aLoadCurrentEntry, bool aUserActivation);
266 // aIndex is the index of the entry which may be removed.
267 // If aKeepNext is true, aIndex is compared to aIndex + 1,
268 // otherwise comparison is done to aIndex - 1.
269 bool RemoveDuplicate(int32_t aIndex, bool aKeepNext);
271 // We need to update entries in docshell and browsing context.
272 // If our docshell is located in parent or 'SH in parent' pref is off we can
273 // update it directly, Otherwise, we have two choices. If the browsing context
274 // that owns the docshell is in the same process as the process who called us
275 // over IPC, then we save entries that need to be updated in a list, and once
276 // we have returned from the IPC call, we update the docshell in the child
277 // process. Otherwise, if the browsing context is in a different process, we
278 // do a nested IPC call to that process to update the docshell in that
279 // process.
280 static void HandleEntriesToSwapInDocShell(mozilla::dom::BrowsingContext* aBC,
281 nsISHEntry* aOldEntry,
282 nsISHEntry* aNewEntry);
284 void UpdateEntryLength(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry,
285 bool aMove);
287 protected:
288 bool mHasOngoingUpdate;
289 nsTArray<nsCOMPtr<nsISHEntry>> mEntries; // entries are never null
290 private:
291 // Track all bfcache entries and evict on expiration.
292 mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
294 int32_t mIndex; // -1 means "no index"
295 int32_t mRequestedIndex; // -1 means "no requested index"
297 // Session History listeners
298 nsAutoTObserverArray<nsWeakPtr, 2> mListeners;
300 nsID mRootDocShellID;
302 // Max viewers allowed total, across all SHistory objects
303 static int32_t sHistoryMaxTotalViewers;
305 // The epoch (and id) tell us what navigations occured within the same
306 // event-loop spin in the child. We need to know this in order to
307 // implement spec requirements for dropping pending navigations when we
308 // do a history navigation, if it's not same-document. Content processes
309 // update the epoch via a runnable on each ::Go (including AsyncGo).
310 uint64_t mEpoch = 0;
311 mozilla::Maybe<mozilla::dom::ContentParentId> mEpochParentId;
314 // CallerWillNotifyHistoryIndexAndLengthChanges is used to prevent
315 // SHistoryChangeNotifier to send automatic index and length updates.
316 // When that is done, it is up to the caller to explicitly send those updates.
317 // This is needed in cases when the update is a reaction to some change in a
318 // child process and child process passes a changeId to the parent side.
319 class MOZ_STACK_CLASS CallerWillNotifyHistoryIndexAndLengthChanges {
320 public:
321 explicit CallerWillNotifyHistoryIndexAndLengthChanges(
322 nsISHistory* aSHistory) {
323 nsSHistory* shistory = static_cast<nsSHistory*>(aSHistory);
324 if (shistory && !shistory->HasOngoingUpdate()) {
325 shistory->SetHasOngoingUpdate(true);
326 mSHistory = shistory;
330 ~CallerWillNotifyHistoryIndexAndLengthChanges() {
331 if (mSHistory) {
332 mSHistory->SetHasOngoingUpdate(false);
336 RefPtr<nsSHistory> mSHistory;
339 inline nsISupports* ToSupports(nsSHistory* aObj) {
340 return static_cast<nsISHistory*>(aObj);
343 #endif /* nsSHistory */