Add session history markdown documentation.
This CL adds session_history.md to explain how NavigationController tracks the back/forward history in each tab. BUG=1015882 Change-Id: I48d7143b513b049ea100ab7efe7d8cc8c41b2cc3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2225373 Commit-Queue: Charlie Reis <creis@chromium.org> Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Cr-Commit-Position: refs/heads/master@{#778546}
This commit is contained in:
125
docs/session_history.md
Normal file
125
docs/session_history.md
Normal file
@ -0,0 +1,125 @@
|
||||
# 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.
|
||||
|
||||
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 main frame documents may not have a corresponding NavigationEntry
|
||||
even after commit (e.g., the initial empty document, per
|
||||
[issue 524208](https://crbug.com/524208)).
|
||||
* Some subframe documents may not have a corresponding FrameNavigationEntry
|
||||
after commit (e.g., see [issue 608402](https://crbug.com/608402)).
|
||||
* FrameNavigationEntries should be shared between NavigationEntries when they
|
||||
do not change (e.g., to support history.replaceState after subframe
|
||||
navigations), but this is not yet supported.
|
||||
See [issue 373041](https://crbug.com/373041).
|
Reference in New Issue
Block a user