chromium/docs/session_history.md

# Session History

A browser's session history keeps track of the navigations in each tab, to
support back/forward navigations and session restore. This is in contrast to
"history" (e.g., `chrome://history`), which tracks the main frame URLs the user
has visited in any tab for the lifetime of a profile.

Chromium tracks the session history of each tab in NavigationController, using a
list of NavigationEntry objects to represent the joint session history items.
Each frame creates _session history items_ as it navigates. A _joint session
history item_ contains the state of each frame of a page at a given point in
time, including things like URL, partially entered form data, scroll position,
etc. Each NavigationEntry uses a tree of FrameNavigationEntries to track this
state.

[TOC]


## Pruning Forward Navigations

If the user goes back and then commits a new navigation, this essentially forks
the joint session history. However, joint session history is tracked as a list
and not as a tree, so the previous forward history is "pruned" and forgotten.
This pruning is performed for all new navigations, unless they commit with
replacement.


## Subframe Navigations

When the first commit occurs within a new subframe of a document, it becomes
part of the existing joint session history item (which we refer to as an "auto
subframe navigation"). The user can't go back to the state before the frame
committed. Any subsequent navigations in the subframe create new joint session
history items (which we refer to as "manual subframe navigations"), such that
clicking back goes back within the subframe.


## Navigating with Replacement

Some types of navigations can replace the previously committed joint session
history item for a frame, rather than creating a new item. These include:

 * `location.replace` (which is usually cross-document, unless it is a fragment
   navigation)
 * `history.replaceState` (which is always same-document)
 * Client redirects
 * The first non-blank URL after the initial empty document (unless the frame
   was explicitly created with `about:blank` as the URL).


## Identifying Same- and Cross-Document Navigations

Each FrameNavigationEntry contains both an _item sequence number_ (ISN) and a
_document sequence number_ (DSN). Same-document navigations create a new session
history item without changing the document, and thus have a new ISN but the same
DSN. Cross-document navigations create a new ISN and DSN. NavigationController
uses these ISNs and DSNs when deciding which frames need to be navigated during
a session history navigation, using a recursive frame tree walk in
`FindFramesToNavigate`.


## Classifying Navigations

Much of the complexity in NavigationController comes from the bookkeeping needed
to track the various types of navigations as they commit (e.g., same-document vs
cross-document, main frame vs subframe, with or without replacement, etc). These
types may lead to different outcomes for whether a new NavigationEntry is
created, whether an existing one is updated vs replaced, and what events are
exposed to observers. This is handled by `ClassifyNavigation`, which determines
which `RendererDidNavigate` helper methods are used when a navigation commits.


## Persistence

The joint session history of a tab is persisted so that tabs can be restored
(e.g., between Chromium restarts, after closing a tab, or on another device).
This requires serializing the state in each NavigationEntry and its tree of
FrameNavigationEntries, using a PageState object and other metadata.
See [Modifying Session History Serialization](modifying_session_history_serialization.md)
for how to safely add new values to be saved and restored.

Not everything in NavigationEntry is persisted. All data members of
NavigationEntryImpl and FrameNavigationEntry should be documented with whether
they are preserved after commit and whether they need to be persisted.

Note that the session history of a tab can also be cloned when duplicating a
tab, or when doing a back/forward/reload navigation in a new tab (such as when
middle-clicking the back/forward/reload button). This involves direct clones of
NavigationEntries rather than persisting and restoring.

## Invariants

 * The `pending_entry_index_` is either -1 or an index into `entries_`. If
   `pending_entry_` is defined and `pending_entry_index_` is -1, then it is a
   new navigation. If `pending_entry_index_` is a valid index into `entries_`,
   then `pending_entry_` must point to that entry and it is a session history
   navigation.
 * Newly created tabs have NavigationControllers with `is_initial_navigation_`
   set to true. They can have `last_committed_entry_index_` defined before the
   first commit, however, when session history is cloned from another tab. (In
   this case, `pending_entry_index_` indicates which entry is going to be
   restored during the initial navigation.)
 * Every FrameNavigationEntry that has committed in the current session (as
   opposed to those that have been restored) must have a SiteInstance.
 * A renderer process can only update FrameNavigationEntries belonging to a
   SiteInstance in that process. This especially includes attacker-controlled
   data like PageState, which could be dangerous to load into a different
   site's process.
 * Any cross-SiteInstance navigation should result in a new NavigationEntry
   with replacement, rather than updating an existing NavigationEntry.


## Caveats

 * Not every NavigationRequest has a pending NavigationEntry. For example,
   subframe navigations do not, and renderer-initiated main frame navigations
   may clear an existing browser-initiated pending NavigationEntry (using
   PendingEntryRef) without replacing it with a new one.
 * Some subframe documents may not have a corresponding FrameNavigationEntry
   after commit (e.g., see [issue 608402](https://crbug.com/608402)).