tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsSHistory.h (15349B)


      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/. */
      6 
      7 #ifndef nsSHistory_h
      8 #define nsSHistory_h
      9 
     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"
     18 
     19 #include "mozilla/dom/ipc/IdType.h"
     20 #include "mozilla/LinkedList.h"
     21 #include "mozilla/UniquePtr.h"
     22 
     23 class nsIDocShell;
     24 class nsDocShell;
     25 class nsSHistoryObserver;
     26 class nsISHEntry;
     27 
     28 namespace mozilla {
     29 namespace dom {
     30 class EntryList;
     31 class LoadSHEntryResult;
     32 }  // namespace dom
     33 }  // namespace mozilla
     34 
     35 class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
     36                   public nsISHistory,
     37                   public nsSupportsWeakReference {
     38 public:
     39  // The timer based history tracker is used to evict bfcache on expiration.
     40  class HistoryTracker final
     41      : public nsExpirationTracker<mozilla::dom::SHEntrySharedParentState, 3> {
     42   public:
     43    explicit HistoryTracker(nsSHistory* aSHistory, uint32_t aTimeout,
     44                            nsIEventTarget* aEventTarget)
     45        : nsExpirationTracker(1000 * aTimeout / 2, "HistoryTracker"_ns,
     46                              aEventTarget) {
     47      MOZ_ASSERT(aSHistory);
     48      mSHistory = aSHistory;
     49    }
     50 
     51   protected:
     52    virtual void NotifyExpired(
     53        mozilla::dom::SHEntrySharedParentState* aObj) override {
     54      RemoveObject(aObj);
     55      mSHistory->EvictExpiredDocumentViewerForEntry(aObj);
     56    }
     57 
     58   private:
     59    // HistoryTracker is owned by nsSHistory; it always outlives HistoryTracker
     60    // so it's safe to use raw pointer here.
     61    nsSHistory* mSHistory;
     62  };
     63 
     64  // Structure used in SetChildHistoryEntry
     65  struct SwapEntriesData {
     66    mozilla::dom::BrowsingContext*
     67        ignoreBC;                // constant; the browsing context to ignore
     68    nsISHEntry* destTreeRoot;    // constant; the root of the dest tree
     69    nsISHEntry* destTreeParent;  // constant; the node under destTreeRoot
     70                                 // whose children will correspond to aEntry
     71  };
     72 
     73  explicit nsSHistory(mozilla::dom::BrowsingContext* aRootBC);
     74  NS_DECL_ISUPPORTS
     75  NS_DECL_NSISHISTORY
     76 
     77  // One time initialization method
     78  static nsresult Startup();
     79  static void Shutdown();
     80  static void UpdatePrefs();
     81 
     82  // Max number of total cached content viewers.  If the pref
     83  // browser.sessionhistory.max_total_viewers is negative, then
     84  // this value is calculated based on the total amount of memory.
     85  // Otherwise, it comes straight from the pref.
     86  static uint32_t GetMaxTotalViewers() { return sHistoryMaxTotalViewers; }
     87 
     88  // Get the root SHEntry from a given entry.
     89  static already_AddRefed<nsISHEntry> GetRootSHEntry(nsISHEntry* aEntry);
     90 
     91  // Callback prototype for WalkHistoryEntries.
     92  // `aEntry` is the child history entry, `aBC` is its corresponding browsing
     93  // context, `aChildIndex` is the child's index in its parent entry, and
     94  // `aData` is the opaque pointer passed to WalkHistoryEntries. Both structs
     95  // that are passed as `aData` to this function have a field
     96  // `aEntriesToUpdate`, which is an array of entries we need to update in
     97  // docshell, if the 'SH in parent' pref is on (which implies that this method
     98  // is executed in the parent)
     99  typedef nsresult (*WalkHistoryEntriesFunc)(nsISHEntry* aEntry,
    100                                             mozilla::dom::BrowsingContext* aBC,
    101                                             int32_t aChildIndex, void* aData);
    102 
    103  // Clone a session history tree for subframe navigation.
    104  // The tree rooted at |aSrcEntry| will be cloned into |aDestEntry|, except
    105  // for the entry with id |aCloneID|, which will be replaced with
    106  // |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
    107  // corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
    108  // have that pointer updated to point to the cloned history entry.
    109  // If aCloneChildren is true then the children of the entry with id
    110  // |aCloneID| will be cloned into |aReplaceEntry|.
    111  static nsresult CloneAndReplace(nsISHEntry* aSrcEntry,
    112                                  mozilla::dom::BrowsingContext* aOwnerBC,
    113                                  uint32_t aCloneID, nsISHEntry* aReplaceEntry,
    114                                  bool aCloneChildren, nsISHEntry** aDestEntry);
    115 
    116  // Child-walking callback for CloneAndReplace
    117  static nsresult CloneAndReplaceChild(nsISHEntry* aEntry,
    118                                       mozilla::dom::BrowsingContext* aOwnerBC,
    119                                       int32_t aChildIndex, void* aData);
    120 
    121  // Child-walking callback for SetHistoryEntry
    122  static nsresult SetChildHistoryEntry(nsISHEntry* aEntry,
    123                                       mozilla::dom::BrowsingContext* aBC,
    124                                       int32_t aEntryIndex, void* aData);
    125 
    126  // For each child of aRootEntry, find the corresponding shell which is
    127  // a child of aBC, and call aCallback. The opaque pointer aData
    128  // is passed to the callback.
    129  static nsresult WalkHistoryEntries(nsISHEntry* aRootEntry,
    130                                     mozilla::dom::BrowsingContext* aBC,
    131                                     WalkHistoryEntriesFunc aCallback,
    132                                     void* aData);
    133 
    134  // This function finds all entries that are contiguous and same-origin with
    135  // the aEntry. And call the aCallback on them, including the aEntry. This only
    136  // works for the root entries. It will do nothing for non-root entries.
    137  static void WalkContiguousEntries(
    138      nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback);
    139  // Same as above, but calls aCallback on the entries in their history order.
    140  // Will stop walking when `aCallback` returns false.
    141  static void WalkContiguousEntriesInOrder(
    142      nsISHEntry* aEntry, const std::function<bool(nsISHEntry*)>& aCallback);
    143 
    144  nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; }
    145 
    146  void NotifyOnHistoryReplaceEntry();
    147 
    148  void RemoveEntries(nsTArray<nsID>& aIDs, int32_t aStartIndex,
    149                     bool* aDidRemove);
    150 
    151  // The size of the window of SHEntries which can have alive viewers in the
    152  // bfcache around the currently active SHEntry.
    153  //
    154  // We try to keep viewers for SHEntries between index - VIEWER_WINDOW and
    155  // index + VIEWER_WINDOW alive.
    156  static const int32_t VIEWER_WINDOW = 3;
    157 
    158  struct LoadEntryResult {
    159    RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext;
    160    RefPtr<nsDocShellLoadState> mLoadState;
    161  };
    162 
    163  MOZ_CAN_RUN_SCRIPT
    164  static void LoadURIs(
    165      const nsTArray<LoadEntryResult>& aLoadResults, bool aCheckForCancelation,
    166      const std::function<void(nsresult)>& aResolver = [](auto) {},
    167      mozilla::dom::BrowsingContext* aTraversable = nullptr);
    168 
    169  MOZ_CAN_RUN_SCRIPT
    170  static void LoadURIOrBFCache(const LoadEntryResult& aLoadEntry);
    171 
    172  // If this doesn't return an error then either aLoadResult is set to nothing,
    173  // in which case the caller should ignore the load, or it returns a valid
    174  // LoadEntryResult in aLoadResult which the caller should use to do the load.
    175  nsresult Reload(uint32_t aReloadFlags,
    176                  nsTArray<LoadEntryResult>& aLoadResults);
    177  nsresult ReloadCurrentEntry(nsTArray<LoadEntryResult>& aLoadResults);
    178  // Passing aSourceBrowsingContext should only be done by
    179  // CanonicalBrowsingContext::HistoryGo, since that corresponds to a call to
    180  // #apply-the-traverse-history-step
    181  nsresult GotoIndex(mozilla::dom::BrowsingContext* aSourceBrowsingContext,
    182                     int32_t aIndex, nsTArray<LoadEntryResult>& aLoadResults,
    183                     bool aSameEpoch, bool aLoadCurrentEntry,
    184                     bool aUserActivation);
    185 
    186  void WindowIndices(int32_t aIndex, int32_t* aOutStartIndex,
    187                     int32_t* aOutEndIndex);
    188  void NotifyListenersDocumentViewerEvicted(uint32_t aNumEvicted);
    189 
    190  int32_t Length() { return int32_t(mEntries.Length()); }
    191  int32_t Index() { return mIndex; }
    192  already_AddRefed<mozilla::dom::BrowsingContext> GetBrowsingContext() {
    193    return mozilla::dom::BrowsingContext::Get(mRootBC);
    194  }
    195  bool HasOngoingUpdate() { return mHasOngoingUpdate; }
    196  void SetHasOngoingUpdate(bool aVal) { mHasOngoingUpdate = aVal; }
    197 
    198  void SetBrowsingContext(mozilla::dom::BrowsingContext* aRootBC) {
    199    uint64_t newID = aRootBC ? aRootBC->Id() : 0;
    200    if (mRootBC != newID) {
    201      mRootBC = newID;
    202    }
    203  }
    204 
    205  int32_t GetTargetIndexForHistoryOperation() {
    206    // When performing a session history operation, such as replace or
    207    // navigation by key that can happen during ongoing history traversals; If
    208    // the requested index is valid, it indicates the loading was triggered by a
    209    // history load, and we should target the entry at requested index instead.
    210    return mRequestedIndex == -1 ? mIndex : mRequestedIndex;
    211  }
    212 
    213  void GetEpoch(uint64_t& aEpoch,
    214                mozilla::Maybe<mozilla::dom::ContentParentId>& aId) const {
    215    aEpoch = mEpoch;
    216    aId = mEpochParentId;
    217  }
    218  void SetEpoch(uint64_t aEpoch,
    219                mozilla::Maybe<mozilla::dom::ContentParentId> aId) {
    220    mEpoch = aEpoch;
    221    mEpochParentId = aId;
    222  }
    223 
    224  void LogHistory();
    225 
    226  mozilla::dom::SessionHistoryEntry* FindAdjacentContiguousEntryFor(
    227      mozilla::dom::SessionHistoryEntry* aEntry, int32_t aSearchDirection);
    228  void ReconstructContiguousEntryListFrom(
    229      mozilla::dom::SessionHistoryEntry* aEntry);
    230  void ReconstructContiguousEntryList();
    231  already_AddRefed<mozilla::dom::EntryList> EntryListFor(const nsID& aID);
    232  void RemoveEntryList(const nsID& aID);
    233 
    234  bool ContainsEntry(nsISHEntry* aEntry);
    235 
    236 protected:
    237  virtual ~nsSHistory();
    238 
    239  uint64_t mRootBC;
    240 
    241 private:
    242  friend class nsSHistoryObserver;
    243 
    244  bool ForEachDifferingEntry(
    245      nsISHEntry* aPrevEntry, nsISHEntry* aNextEntry,
    246      mozilla::dom::BrowsingContext* aParent,
    247      const std::function<void(nsISHEntry*, mozilla::dom::BrowsingContext*)>&
    248          aCallback);
    249  void InitiateLoad(mozilla::dom::BrowsingContext* aSourceBrowsingContext,
    250                    nsISHEntry* aFrameEntry,
    251                    mozilla::dom::BrowsingContext* aFrameBC, long aLoadType,
    252                    nsTArray<LoadEntryResult>& aLoadResult,
    253                    bool aLoadCurrentEntry, bool aUserActivation,
    254                    int32_t aOffset);
    255 
    256  nsresult LoadEntry(mozilla::dom::BrowsingContext* aSourceBrowsingContext,
    257                     int32_t aIndex, long aLoadType, uint32_t aHistCmd,
    258                     nsTArray<LoadEntryResult>& aLoadResults, bool aSameEpoch,
    259                     bool aLoadCurrentEntry, bool aUserActivation);
    260 
    261  // Find the history entry for a given bfcache entry. It only looks up between
    262  // the range where alive viewers may exist (i.e nsSHistory::VIEWER_WINDOW).
    263  nsresult FindEntryForBFCache(mozilla::dom::SHEntrySharedParentState* aEntry,
    264                               nsISHEntry** aResult, int32_t* aResultIndex);
    265 
    266  // Evict content viewers in this window which don't lie in the "safe" range
    267  // around aIndex.
    268  virtual void EvictOutOfRangeWindowDocumentViewers(int32_t aIndex);
    269 
    270 public:
    271  void EvictDocumentViewerForEntry(nsISHEntry* aEntry);
    272 
    273 private:
    274  static void GloballyEvictDocumentViewers();
    275  static void GloballyEvictAllDocumentViewers();
    276 
    277  // Calculates a max number of total
    278  // content viewers to cache, based on amount of total memory
    279  static uint32_t CalcMaxTotalViewers();
    280 
    281  nsresult LoadNextPossibleEntry(
    282      mozilla::dom::BrowsingContext* aSourceBrowsingContext, int32_t aNewIndex,
    283      long aLoadType, uint32_t aHistCmd,
    284      nsTArray<LoadEntryResult>& aLoadResults, bool aLoadCurrentEntry,
    285      bool aUserActivation);
    286 
    287  // aIndex is the index of the entry which may be removed.
    288  // If aKeepNext is true, aIndex is compared to aIndex + 1,
    289  // otherwise comparison is done to aIndex - 1.
    290  bool RemoveDuplicate(int32_t aIndex, bool aKeepNext);
    291 
    292  // We need to update entries in docshell and browsing context.
    293  // If our docshell is located in parent or 'SH in parent' pref is off we can
    294  // update it directly, Otherwise, we have two choices. If the browsing context
    295  // that owns the docshell is in the same process as the process who called us
    296  // over IPC, then we save entries that need to be updated in a list, and once
    297  // we have returned from the IPC call, we update the docshell in the child
    298  // process. Otherwise, if the browsing context is in a different process, we
    299  // do a nested IPC call to that process to update the docshell in that
    300  // process.
    301  static void HandleEntriesToSwapInDocShell(mozilla::dom::BrowsingContext* aBC,
    302                                            nsISHEntry* aOldEntry,
    303                                            nsISHEntry* aNewEntry);
    304 
    305  void UpdateEntryLength(nsISHEntry* aOldEntry, nsISHEntry* aNewEntry,
    306                         bool aMove);
    307 
    308 protected:
    309  bool mHasOngoingUpdate;
    310  nsTArray<nsCOMPtr<nsISHEntry>> mEntries;  // entries are never null
    311 private:
    312  // Track all bfcache entries and evict on expiration.
    313  mozilla::UniquePtr<HistoryTracker> mHistoryTracker;
    314 
    315  int32_t mIndex;           // -1 means "no index"
    316  int32_t mRequestedIndex;  // -1 means "no requested index"
    317 
    318  // Session History listeners
    319  nsAutoTObserverArray<nsWeakPtr, 2> mListeners;
    320 
    321  nsID mRootDocShellID;
    322 
    323  // Max viewers allowed total, across all SHistory objects
    324  static int32_t sHistoryMaxTotalViewers;
    325 
    326  // The epoch (and id) tell us what navigations occured within the same
    327  // event-loop spin in the child.  We need to know this in order to
    328  // implement spec requirements for dropping pending navigations when we
    329  // do a history navigation, if it's not same-document.  Content processes
    330  // update the epoch via a runnable on each ::Go (including AsyncGo).
    331  uint64_t mEpoch = 0;
    332  mozilla::Maybe<mozilla::dom::ContentParentId> mEpochParentId;
    333 
    334  // Session history entries grouped by DocshellID, which are deduplicated by
    335  // SessionHistoryEntry ID.
    336  nsTHashMap<nsIDHashKey, mozilla::WeakPtr<mozilla::dom::EntryList>>
    337      mEntryLists;
    338 };
    339 
    340 // CallerWillNotifyHistoryIndexAndLengthChanges is used to prevent
    341 // SHistoryChangeNotifier to send automatic index and length updates.
    342 // When that is done, it is up to the caller to explicitly send those updates.
    343 // This is needed in cases when the update is a reaction to some change in a
    344 // child process and child process passes a changeId to the parent side.
    345 class MOZ_STACK_CLASS CallerWillNotifyHistoryIndexAndLengthChanges {
    346 public:
    347  explicit CallerWillNotifyHistoryIndexAndLengthChanges(
    348      nsISHistory* aSHistory) {
    349    nsSHistory* shistory = static_cast<nsSHistory*>(aSHistory);
    350    if (shistory && !shistory->HasOngoingUpdate()) {
    351      shistory->SetHasOngoingUpdate(true);
    352      mSHistory = shistory;
    353    }
    354  }
    355 
    356  ~CallerWillNotifyHistoryIndexAndLengthChanges() {
    357    if (mSHistory) {
    358      mSHistory->SetHasOngoingUpdate(false);
    359    }
    360  }
    361 
    362  RefPtr<nsSHistory> mSHistory;
    363 };
    364 
    365 inline nsISupports* ToSupports(nsSHistory* aObj) {
    366  return static_cast<nsISHistory*>(aObj);
    367 }
    368 
    369 #endif /* nsSHistory */