#include "content/browser/renderer_host/page_lifecycle_state_manager.h"
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/render_process_host.h"
#include "services/service_manager/public/cpp/interface_provider.h"
namespace {
#if defined(ADDRESS_SANITIZER)
constexpr base::TimeDelta kBackForwardCacheTimeout = base::Seconds(12);
#else
constexpr base::TimeDelta kBackForwardCacheTimeout = base::Seconds(3);
#endif
base::TimeDelta GetBackForwardCacheEntryTimeout() {
if (base::FeatureList::IsEnabled(features::kBackForwardCacheEntryTimeout)) {
return kBackForwardCacheTimeout;
} else {
return base::TimeDelta::Max();
}
}
}
namespace content {
PageLifecycleStateManager::TestDelegate::TestDelegate() = default;
PageLifecycleStateManager::TestDelegate::~TestDelegate() = default;
void PageLifecycleStateManager::TestDelegate::OnLastAcknowledgedStateChanged(
const blink::mojom::PageLifecycleState& old_state,
const blink::mojom::PageLifecycleState& new_state) {}
void PageLifecycleStateManager::TestDelegate::OnUpdateSentToRenderer(
const blink::mojom::PageLifecycleState& new_state) {}
void PageLifecycleStateManager::TestDelegate::OnDeleted() {}
PageLifecycleStateManager::PageLifecycleStateManager(
RenderViewHostImpl* render_view_host_impl,
blink::mojom::PageVisibilityState frame_tree_visibility)
: frame_tree_visibility_(frame_tree_visibility),
render_view_host_impl_(render_view_host_impl) {
last_acknowledged_state_ = CalculatePageLifecycleState();
last_state_sent_to_renderer_ = last_acknowledged_state_.Clone();
}
PageLifecycleStateManager::~PageLifecycleStateManager() {
if (test_delegate_)
test_delegate_->OnDeleted();
}
void PageLifecycleStateManager::SetIsFrozen(bool frozen) {
if (is_set_frozen_called_ == frozen)
return;
is_set_frozen_called_ = frozen;
SendUpdatesToRendererIfNeeded(
nullptr, base::NullCallback());
}
void PageLifecycleStateManager::SetFrameTreeVisibility(
blink::mojom::PageVisibilityState visibility) {
if (frame_tree_visibility_ == visibility)
return;
frame_tree_visibility_ = visibility;
SendUpdatesToRendererIfNeeded(
nullptr, base::NullCallback());
}
void PageLifecycleStateManager::SetIsInBackForwardCache(
bool is_in_back_forward_cache,
blink::mojom::PageRestoreParamsPtr page_restore_params) {
if (is_in_back_forward_cache_ == is_in_back_forward_cache)
return;
DCHECK(is_in_back_forward_cache ||
!last_acknowledged_state_->eviction_enabled);
is_in_back_forward_cache_ = is_in_back_forward_cache;
eviction_enabled_ = is_in_back_forward_cache;
if (is_in_back_forward_cache) {
back_forward_cache_timeout_monitor_.Start(
FROM_HERE, GetBackForwardCacheEntryTimeout(),
base::BindOnce(&PageLifecycleStateManager::OnBackForwardCacheTimeout,
weak_ptr_factory_.GetWeakPtr()));
pagehide_dispatch_ = blink::mojom::PagehideDispatch::kDispatchedPersisted;
} else {
DCHECK(page_restore_params);
pagehide_dispatch_ = blink::mojom::PagehideDispatch::kNotDispatched;
}
SendUpdatesToRendererIfNeeded(std::move(page_restore_params),
base::NullCallback());
}
blink::mojom::PageLifecycleStatePtr
PageLifecycleStateManager::SetPagehideDispatchDuringNewPageCommit(
bool persisted) {
pagehide_dispatch_ =
persisted ? blink::mojom::PagehideDispatch::kDispatchedPersisted
: blink::mojom::PagehideDispatch::kDispatchedNotPersisted;
last_state_sent_to_renderer_ = CalculatePageLifecycleState();
DCHECK_EQ(last_state_sent_to_renderer_->visibility,
blink::mojom::PageVisibilityState::kHidden);
return last_state_sent_to_renderer_.Clone();
}
void PageLifecycleStateManager::DidSetPagehideDispatchDuringNewPageCommit(
blink::mojom::PageLifecycleStatePtr acknowledged_state) {
DCHECK_EQ(acknowledged_state->visibility,
blink::mojom::PageVisibilityState::kHidden);
DCHECK_NE(acknowledged_state->pagehide_dispatch,
blink::mojom::PagehideDispatch::kNotDispatched);
OnPageLifecycleChangedAck(std::move(acknowledged_state),
base::NullCallback());
}
void PageLifecycleStateManager::SetIsLeavingBackForwardCache(
base::OnceClosure done_cb) {
DCHECK(is_in_back_forward_cache_);
eviction_enabled_ = false;
SendUpdatesToRendererIfNeeded(nullptr, std::move(done_cb));
}
bool PageLifecycleStateManager::RendererExpectedToSendChannelAssociatedIpcs()
const {
DCHECK(!eviction_enabled_ || is_in_back_forward_cache_);
return !eviction_enabled_ || !last_acknowledged_state_->eviction_enabled;
}
void PageLifecycleStateManager::SendUpdatesToRendererIfNeeded(
blink::mojom::PageRestoreParamsPtr page_restore_params,
base::OnceClosure done_cb) {
if (!render_view_host_impl_->GetAssociatedPageBroadcast()) {
if (done_cb) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(done_cb));
}
return;
}
auto new_state = CalculatePageLifecycleState();
if (last_state_sent_to_renderer_ &&
last_state_sent_to_renderer_.Equals(new_state)) {
}
last_state_sent_to_renderer_ = new_state.Clone();
auto state = new_state.Clone();
if (test_delegate_)
test_delegate_->OnUpdateSentToRenderer(*last_state_sent_to_renderer_);
render_view_host_impl_->GetAssociatedPageBroadcast()->SetPageLifecycleState(
std::move(state), std::move(page_restore_params),
base::BindOnce(&PageLifecycleStateManager::OnPageLifecycleChangedAck,
weak_ptr_factory_.GetWeakPtr(), std::move(new_state),
std::move(done_cb)));
}
blink::mojom::PageLifecycleStatePtr
PageLifecycleStateManager::CalculatePageLifecycleState() {
auto state = blink::mojom::PageLifecycleState::New();
state->is_in_back_forward_cache = is_in_back_forward_cache_;
state->is_frozen = is_in_back_forward_cache_ ? true : is_set_frozen_called_;
state->pagehide_dispatch = pagehide_dispatch_;
state->visibility =
(is_in_back_forward_cache_ ||
pagehide_dispatch_ != blink::mojom::PagehideDispatch::kNotDispatched)
? blink::mojom::PageVisibilityState::kHidden
: frame_tree_visibility_;
state->eviction_enabled = eviction_enabled_;
return state;
}
void PageLifecycleStateManager::OnPageLifecycleChangedAck(
blink::mojom::PageLifecycleStatePtr acknowledged_state,
base::OnceClosure done_cb) {
blink::mojom::PageLifecycleStatePtr old_state =
std::move(last_acknowledged_state_);
last_acknowledged_state_ = std::move(acknowledged_state);
if (last_acknowledged_state_->is_in_back_forward_cache)
did_receive_back_forward_cache_ack_ = true;
render_view_host_impl_->MaybeEvictFromBackForwardCache();
render_view_host_impl_->EnforceBackForwardCacheSizeLimit();
if (last_acknowledged_state_->is_in_back_forward_cache) {
back_forward_cache_timeout_monitor_.Stop();
}
if (test_delegate_) {
test_delegate_->OnLastAcknowledgedStateChanged(*old_state,
*last_acknowledged_state_);
}
if (done_cb)
std::move(done_cb).Run();
}
void PageLifecycleStateManager::OnBackForwardCacheTimeout() {
DCHECK(!last_acknowledged_state_->is_in_back_forward_cache);
render_view_host_impl_->OnBackForwardCacheTimeout();
back_forward_cache_timeout_monitor_.Stop();
}
void PageLifecycleStateManager::SetDelegateForTesting(
PageLifecycleStateManager::TestDelegate* test_delegate) {
DCHECK(!test_delegate_ || !test_delegate);
test_delegate_ = test_delegate;
}
}