Bug 1719035 [wpt PR 29572] - Address flakiness in import-css-module-basic.html, a...
[gecko.git] / docshell / shistory / nsSHistory.h
blob2bf059847dfe887494e1a1d139aad62d7a8eadb8
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->EvictExpiredContentViewerForEntry(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 = false, bool aUserActivation = false);
170 void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex,
171 int32_t* aOutEndIndex);
172 void NotifyListenersContentViewerEvicted(uint32_t aNumEvicted);
174 int32_t Length() { return int32_t(mEntries.Length()); }
175 int32_t Index() { return mIndex; }
176 mozilla::dom::BrowsingContext* GetBrowsingContext() { return mRootBC; }
177 bool HasOngoingUpdate() { return mHasOngoingUpdate; }
178 void SetHasOngoingUpdate(bool aVal) { mHasOngoingUpdate = aVal; }
180 void SetBrowsingContext(mozilla::dom::BrowsingContext* aRootBC) {
181 if (mRootBC == aRootBC) {
182 return;
184 mRootBC = aRootBC;
185 UpdateRootBrowsingContextState();
188 int32_t GetIndexForReplace() {
189 // Replace current entry in session history; If the requested index is
190 // valid, it indicates the loading was triggered by a history load, and
191 // we should replace the entry at requested index instead.
192 return mRequestedIndex == -1 ? mIndex : mRequestedIndex;
195 // Update the root browsing context state when adding, removing or
196 // replacing entries.
197 void UpdateRootBrowsingContextState();
199 void GetEpoch(uint64_t& aEpoch,
200 mozilla::Maybe<mozilla::dom::ContentParentId>& aId) const {
201 aEpoch = mEpoch;
202 aId = mEpochParentId;
204 void SetEpoch(uint64_t aEpoch,
205 mozilla::Maybe<mozilla::dom::ContentParentId> aId) {
206 mEpoch = aEpoch;
207 mEpochParentId = aId;
210 void LogHistory();
212 protected:
213 virtual ~nsSHistory();
215 // Weak reference. Do not refcount this.
216 mozilla::dom::BrowsingContext* mRootBC;
218 private:
219 friend class nsSHistoryObserver;
221 bool LoadDifferingEntries(nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry,
222 mozilla::dom::BrowsingContext* aParent,
223 long aLoadType,
224 nsTArray<LoadEntryResult>& aLoadResults,
225 bool aUserActivation);
226 void InitiateLoad(nsISHEntry* aFrameEntry,
227 mozilla::dom::BrowsingContext* aFrameBC, long aLoadType,
228 nsTArray<LoadEntryResult>& aLoadResult,
229 bool aUserActivation);
231 nsresult LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd,
232 nsTArray<LoadEntryResult>& aLoadResults,
233 bool aSameEpoch = false, bool aUserActivation = false);
235 // Find the history entry for a given bfcache entry. It only looks up between
236 // the range where alive viewers may exist (i.e nsSHistory::VIEWER_WINDOW).
237 nsresult FindEntryForBFCache(mozilla::dom::SHEntrySharedParentState* aEntry,
238 nsISHEntry** aResult, int32_t* aResultIndex);
240 // Evict content viewers in this window which don't lie in the "safe" range
241 // around aIndex.
242 virtual void EvictOutOfRangeWindowContentViewers(int32_t aIndex);
243 void EvictContentViewerForEntry(nsISHEntry* aEntry);
244 static void GloballyEvictContentViewers();
245 static void GloballyEvictAllContentViewers();
247 // Calculates a max number of total
248 // content viewers to cache, based on amount of total memory
249 static uint32_t CalcMaxTotalViewers();
251 nsresult LoadNextPossibleEntry(int32_t aNewIndex, long aLoadType,
252 uint32_t aHistCmd,
253 nsTArray<LoadEntryResult>& aLoadResults,
254 bool aUserActivation);
256 // aIndex is the index of the entry which may be removed.
257 // If aKeepNext is true, aIndex is compared to aIndex + 1,
258 // otherwise comparison is done to aIndex - 1.
259 bool RemoveDuplicate(int32_t aIndex, bool aKeepNext);
261 // We need to update entries in docshell and browsing context.
262 // If our docshell is located in parent or 'SH in parent' pref is off we can
263 // update it directly, Otherwise, we have two choices. If the browsing context
264 // that owns the docshell is in the same process as the process who called us
265 // over IPC, then we save entries that need to be updated in a list, and once
266 // we have returned from the IPC call, we update the docshell in the child
267 // process. Otherwise, if the browsing context is in a different process, we
268 // do a nested IPC call to that process to update the docshell in that
269 // process.
270 static void HandleEntriesToSwapInDocShell(mozilla::dom::BrowsingContext* aBC,
271 nsISHEntry* aOldEntry,
272 nsISHEntry* aNewEntry);
274 void UpdateEntryLength(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry,
275 bool aMove);
277 protected:
278 bool mHasOngoingUpdate;
279 nsTArray<nsCOMPtr<nsISHEntry>> mEntries; // entries are never null
280 private:
281 // Track all bfcache entries and evict on expiration.
282 mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
284 int32_t mIndex; // -1 means "no index"
285 int32_t mRequestedIndex; // -1 means "no requested index"
287 // Session History listeners
288 nsAutoTObserverArray<nsWeakPtr, 2> mListeners;
290 nsID mRootDocShellID;
292 // Max viewers allowed total, across all SHistory objects
293 static int32_t sHistoryMaxTotalViewers;
295 // The epoch (and id) tell us what navigations occured within the same
296 // event-loop spin in the child. We need to know this in order to
297 // implement spec requirements for dropping pending navigations when we
298 // do a history navigation, if it's not same-document. Content processes
299 // update the epoch via a runnable on each ::Go (including AsyncGo).
300 uint64_t mEpoch = 0;
301 mozilla::Maybe<mozilla::dom::ContentParentId> mEpochParentId;
304 // CallerWillNotifyHistoryIndexAndLengthChanges is used to prevent
305 // SHistoryChangeNotifier to send automatic index and length updates.
306 // When that is done, it is up to the caller to explicitly send those updates.
307 // This is needed in cases when the update is a reaction to some change in a
308 // child process and child process passes a changeId to the parent side.
309 class MOZ_STACK_CLASS CallerWillNotifyHistoryIndexAndLengthChanges {
310 public:
311 explicit CallerWillNotifyHistoryIndexAndLengthChanges(
312 nsISHistory* aSHistory) {
313 nsSHistory* shistory = static_cast<nsSHistory*>(aSHistory);
314 if (shistory && !shistory->HasOngoingUpdate()) {
315 shistory->SetHasOngoingUpdate(true);
316 mSHistory = shistory;
320 ~CallerWillNotifyHistoryIndexAndLengthChanges() {
321 if (mSHistory) {
322 mSHistory->SetHasOngoingUpdate(false);
326 RefPtr<nsSHistory> mSHistory;
329 inline nsISupports* ToSupports(nsSHistory* aObj) {
330 return static_cast<nsISHistory*>(aObj);
333 #endif /* nsSHistory */