blob: 6cbdbf1399ee72e4fd938b37d56661fd07a4643e [file] [log] [blame] [view]
Sylvain Defresne3e6c4902021-11-04 14:46:391# Unrealized `WebState`
2
Sylvain Defresne425a00b2022-07-20 13:54:563> **Status**: launched.
Sylvain Defresne3e6c4902021-11-04 14:46:394
5On iOS, each tab is implemented by a `WebState` and some TabHelpers. As users
6can have many tabs open at the same time, but only few of them visible, an
7optimisation to reduce the memory pressure is to allow `WebState`s to exist
8in an incomplete state upon session restoration.
9
10This incomplete state is called "unrealized". When in the unrealized state,
11a `WebState` will not have a corresponding WKWebView, nor any of the objects
12that implement navigation (such as NavigationManager, ...).
13
14WebState can transition from "unrealized" to "realized" either lazily when
15the client code request a functionality that cannot be provided in that
16state (such as accessing the NavigationManager, displaying, ...) or it can
17be forced by calling the `WebState::ForceRealized()` method. This is a one
18way transition, it is not possible for a "realized" WebState to get back
19into the "unrealized" state (at least in the initial implementation).
20
21To avoid unnecessary transition to the "realized" state , the
22`WebState::IsRealized()` method can be called. This can be used by TabHelpers
23to delay their initialisation until the `WebState` become "realized". To be
24informed of the transition, they can listen for the
25`WebStateObserver::WebStateRealized()` event which will be invoked upon
26transition of the `WebState` from "unrealized" to "realized".
27
28## Features available on "unrealized" WebState
29
30An "unrealized" `WebState` supports the following features:
31
32- registering and removing Observers
33- registering and removing WebStatePolicyDecider
Sylvain Defresne3e6c4902021-11-04 14:46:3934- `const` property getters (*)
35- retrieving saved state (**)
36- attaching tab helpers
37
38(*) : all of the `const` property getters can be called on an "unrealized"
39`WebState` but they may return a default value (`false`, `nil`, `nullptr`,
40empty string, ... if the information cannot be retrieved from the serialised
41state).
42
43(**): retrieving the saved state is supported to save the session (as the
44code to save the session currently needs the state of all `WebState`s).
45
46## When are `WebState` created in "unrealized" state
47
48The `WebState` are usually created in the "unrealized" state when a session
49is restored. The reason is that restoring a session may create many `WebState`
50when only few of them will be immediately used, while other ways to create a
51`WebState` (opening a new tab, preloading, ...) lead to immediate use.
52
53As seen previously, `WebState` can only transition from the "unrealized" to
54the "realized" state. This means that `WebState` in the "unrealized" state
55would have been created directly in that state.
56
57The `WebState` are not necessarily created in the "unrealized" state upon
58session restoration. This is controlled by the `enable_unrealized_web_states`
59gn variable (compilation) and the `#lazily-create-web-state-on-restoration`
60flag (runtime).
61
62The transition to "realized" state does not require any action from the client
63of the `WebState`. Only internal state of the `WebState` will be affected. The
64observers registered will still be valid, as are the policy decider, the script
65callbacks, ... The client code may want to listen to the transition to activate
66itself if its behaviour depends on internal objects of the `WebState` (such as
67the `NavigationManager`).
68
69## Example of `FindTabHelper`
70
71`FindTabHelper` is a TabHelper that implements the "find in page" feature. It
72wants to create a `FindInPageController` which needs a "realized" `WebState`.
73To support "unrealized" `WebState`, the creation of the `FindInPageController`
74is delayed until the `WebState` transitions to "realized" state.
75
76This is done in the following way:
77
78```cpp
79FindTabHelper::FindTabHelper(web::WebState* web_state) {
80 DCHECK(web_state);
81 web_state_observation_.Observe(web_state);
82 if (web_state->IsRealized()) {
83 CreateFindInPageController(web_state);
84 }
85}
86
87void FindTabHelper::SetResponseDelegate(
88 id<FindInPageResponseDelegate> response_delegate) {
89 if (!_controller) {
90 response_delegate_ = response_delegate;
91 } else {
92 controller_.responseDelegate = response_delegate;
93 }
94}
95
96bool FindTabHelper::CurrentPageSupportsFindInPage() const {
97 // As sending a message to `nil` returns the default value for a type
98 // (`false` for `bool`), it is not needed to check `controller_` first.
99 return [controller_ canFindInPage];
100}
101
102// Other FindTabHelper methods that implement the "find in page" feature.
103
104void FindTabHelper::CreateFindInPageController(web::WebState* web_state) {
105 DCHECK(!controller_);
106 DCHECK(web_state->IsRealized());
107 controller_ = [[FindInPageController alloc] initWithWebState:web_state];
108 if (response_delegate_) {
109 controller_.responseDelegate = response_delegate_;
110 response_delegate_ = nil;
111 }
112}
113
114void FindTabHelper::WebStateRealized(web::WebState* web_state) {
115 CreateFindInPageController(web_state);
116}
117```