WebState
Status: launched.
On iOS, each tab is implemented by a WebState
and some TabHelpers. As users can have many tabs open at the same time, but only few of them visible, an optimisation to reduce the memory pressure is to allow WebState
s to exist in an incomplete state upon session restoration.
This incomplete state is called “unrealized”. When in the unrealized state, a WebState
will not have a corresponding WKWebView, nor any of the objects that implement navigation (such as NavigationManager, ...).
WebState can transition from “unrealized” to “realized” either lazily when the client code request a functionality that cannot be provided in that state (such as accessing the NavigationManager, displaying, ...) or it can be forced by calling the WebState::ForceRealized()
method. This is a one way transition, it is not possible for a “realized” WebState to get back into the “unrealized” state (at least in the initial implementation).
To avoid unnecessary transition to the “realized” state , the WebState::IsRealized()
method can be called. This can be used by TabHelpers to delay their initialisation until the WebState
become “realized”. To be informed of the transition, they can listen for the WebStateObserver::WebStateRealized()
event which will be invoked upon transition of the WebState
from “unrealized” to “realized”.
An “unrealized” WebState
supports the following features:
const
property getters (*)(*) : all of the const
property getters can be called on an “unrealized” WebState
but they may return a default value (false
, nil
, nullptr
, empty string, ... if the information cannot be retrieved from the serialised state).
(**): retrieving the saved state is supported to save the session (as the code to save the session currently needs the state of all WebState
s).
WebState
created in “unrealized” stateThe WebState
are usually created in the “unrealized” state when a session is restored. The reason is that restoring a session may create many WebState
when only few of them will be immediately used, while other ways to create a WebState
(opening a new tab, preloading, ...) lead to immediate use.
As seen previously, WebState
can only transition from the “unrealized” to the “realized” state. This means that WebState
in the “unrealized” state would have been created directly in that state.
The WebState
are not necessarily created in the “unrealized” state upon session restoration. This is controlled by the enable_unrealized_web_states
gn variable (compilation) and the #lazily-create-web-state-on-restoration
flag (runtime).
The transition to “realized” state does not require any action from the client of the WebState
. Only internal state of the WebState
will be affected. The observers registered will still be valid, as are the policy decider, the script callbacks, ... The client code may want to listen to the transition to activate itself if its behaviour depends on internal objects of the WebState
(such as the NavigationManager
).
FindTabHelper
FindTabHelper
is a TabHelper that implements the “find in page” feature. It wants to create a FindInPageController
which needs a “realized” WebState
. To support “unrealized” WebState
, the creation of the FindInPageController
is delayed until the WebState
transitions to “realized” state.
This is done in the following way:
FindTabHelper::FindTabHelper(web::WebState* web_state) { DCHECK(web_state); web_state_observation_.Observe(web_state); if (web_state->IsRealized()) { CreateFindInPageController(web_state); } } void FindTabHelper::SetResponseDelegate( id<FindInPageResponseDelegate> response_delegate) { if (!_controller) { response_delegate_ = response_delegate; } else { controller_.responseDelegate = response_delegate; } } bool FindTabHelper::CurrentPageSupportsFindInPage() const { // As sending a message to `nil` returns the default value for a type // (`false` for `bool`), it is not needed to check `controller_` first. return [controller_ canFindInPage]; } // Other FindTabHelper methods that implement the "find in page" feature. void FindTabHelper::CreateFindInPageController(web::WebState* web_state) { DCHECK(!controller_); DCHECK(web_state->IsRealized()); controller_ = [[FindInPageController alloc] initWithWebState:web_state]; if (response_delegate_) { controller_.responseDelegate = response_delegate_; response_delegate_ = nil; } } void FindTabHelper::WebStateRealized(web::WebState* web_state) { CreateFindInPageController(web_state); }