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);
}