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