blob: 4ba1b3168ae6c3a86e8e914098db4866989fc288 [file] [log] [blame] [view]
shivanigithub2190650b2022-10-13 02:01:321# History manipulation intervention in Chromium
2
3Reference: [PSA on blink-dev](https://groups.google.com/a/chromium.org/g/blink-dev/c/T8d4_BRb2xQ/m/WSdOiOFcBAAJ)
4
5## Summary
6Some pages make it difficult or impossible for the user to use the browser back
7button to go back to the page they came from. Pages accomplish this using
8redirects or by manipulating the browser history, resulting in an
9abusive/annoying user experience.
10
11The history manipulation intervention mitigates such abuse by making the
12browsers back button skip over pages that added history entries or redirected
13the user without ever getting a user activation. Note that the intervention only
14impacts the browser back/forward buttons and not the `history.back()/forward()`
15APIs.
16
17Heres an example:
181) User is on a.com and clicks to go to b.com
192) b.com adds a history entry using `pushState` or navigates the user to another
20page (c.com) without ever getting a user activation.
213) If the user presses back, the browser will skip b.com and go back to a.com
22instead.
23
24## Spec
25Because this only impacts browser UI, this is allowed by the spec, which only
26governs the behavior of `history.back/forward`.
27However, it might be good to spec this anyway, so that users get consistent
28experiences in all browsers. That work is tracked at
29https://github.com/whatwg/html/issues/7832
30
31## Invariants
Shivani Sharmaeef521b2024-01-18 13:03:5632At a high level, the intervention ensures the back/forward buttons always
33navigate to a page the user either navigated to or interacted with. It
34guarantees the following invariants:
shivanigithub2190650b2022-10-13 02:01:32351. Only back/forward navigations triggered by the back/forward buttons will ever
36 skip history entries. This ensures that the history API's behavior is
37 unaffected.
382. The intervention marks a history entry as skippable if the document creates
39 another history entry without a user activation.
403. If a document receives a user activation (before or after creating history
41 entries), its history entry is not skippable. With an activation, the
42 document can create many unskippable same-document history entries, until
Shivani Sharmaeef521b2024-01-18 13:03:5643 either a cross-document navigation or a back/forward occurs. Note that
44 same-document back/forwards do not normally reset any prior user activation,
45 but the intervention stops honoring such activations for creating new
46 entries until a new activation is received, per https://crbug.com/1248529.
shivanigithub2190650b2022-10-13 02:01:32474. All same-document history entries will have the same skippable state. When
48 marking an entry unskippable after a user activation, this ensures that the
49 rest of the document's entries work as well. When marking an entry as
50 skippable, this ensures that all entries for the offending document will be
51 skipped.
525. Revisiting a skippable history entry does not change its skippable status,
53 unless it receives a user activation. This ensures that history.back() will
54 not bypass the intervention, per https://crbug.com/1121293.
556. The intervention applies to history entries created by subframes as well. A
56 user activation on any frame on the page is sufficient to make the entry
57 unskippable, per https://crbug.com/953056.
58
59## Details
601. The intervention works by setting the `should_skip_on_back_forward_ui_`
61 member for a `NavigationEntryImpl` object. The member is initially set to
62 false, and it is set to true if any document in the page adds a history entry
63 without having a user activation.
642. `NavigationController::CanGoBack()` will return false if all entries are
65 marked to be skipped on back/forward UI. On desktop this leads to the back
66 button being disabled. On Android, pressing the back button will close the
67 current tab and a previous tab could be shown as it would normally happen on
68 Android when the back button is pressed from the first entry of a tab.
693. The oldest `NavigationEntryImpl` that is marked as skippable is the one
70 that is pruned if max entry count is reached.