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.
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.
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.
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)about:blank
as the URL).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
.
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.
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.
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.
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.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.)