#include "content/browser/renderer_host/delegated_frame_host.h"
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "content/browser/compositor/surface_utils.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/public/common/content_switches.h"
#include "third_party/blink/public/mojom/widget/record_content_to_visible_time_request.mojom.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/dip_util.h"
namespace content {
namespace {
constexpr float kFrameContentCaptureQuality = 0.4f;
}
DelegatedFrameHost::DelegatedFrameHost(const viz::FrameSinkId& frame_sink_id,
DelegatedFrameHostClient* client,
bool should_register_frame_sink_id)
: frame_sink_id_(frame_sink_id),
client_(client),
should_register_frame_sink_id_(should_register_frame_sink_id),
host_frame_sink_manager_(GetHostFrameSinkManager()),
frame_evictor_(std::make_unique<viz::FrameEvictor>(this)) {
CHECK(host_frame_sink_manager_);
frame_evictor_->SetVisible(client_->DelegatedFrameHostIsVisible());
stale_content_layer_ =
std::make_unique<ui::Layer>(ui::LayerType::LAYER_SOLID_COLOR);
stale_content_layer_->SetVisible(false);
stale_content_layer_->SetColor(SK_ColorTRANSPARENT);
}
DelegatedFrameHost::~DelegatedFrameHost() {
CHECK(!compositor_);
CHECK(host_frame_sink_manager_);
if (owns_frame_sink_id_) {
host_frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_, this, {});
}
}
void DelegatedFrameHost::AddObserverForTesting(Observer* observer) {
observers_.AddObserver(observer);
}
void DelegatedFrameHost::RemoveObserverForTesting(Observer* observer) {
observers_.RemoveObserver(observer);
}
void DelegatedFrameHost::WasShown(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Size& new_dip_size,
blink::mojom::RecordContentToVisibleTimeRequestPtr
record_tab_switch_time_request) {
SetFrameEvictionStateAndNotifyObservers(FrameEvictionState::kNotStarted);
frame_evictor_->SetVisible(true);
if (record_tab_switch_time_request && compositor_) {
compositor_->RequestSuccessfulPresentationTimeForNextFrame(
tab_switch_time_recorder_.TabWasShown(
true ,
std::move(record_tab_switch_time_request)));
}
EmbedSurface(new_local_surface_id, new_dip_size,
cc::DeadlinePolicy::UseDefaultDeadline());
if (stale_content_layer_->has_external_content()) {
stale_content_layer_->SetShowSolidColorContent();
stale_content_layer_->SetVisible(false);
}
}
void DelegatedFrameHost::RequestSuccessfulPresentationTimeForNextFrame(
blink::mojom::RecordContentToVisibleTimeRequestPtr visible_time_request) {
CHECK(visible_time_request);
if (!compositor_)
return;
compositor_->RequestSuccessfulPresentationTimeForNextFrame(
tab_switch_time_recorder_.TabWasShown(true ,
std::move(visible_time_request)));
}
void DelegatedFrameHost::CancelSuccessfulPresentationTimeRequest() {
tab_switch_time_recorder_.TabWasHidden();
}
bool DelegatedFrameHost::HasSavedFrame() const {
return frame_evictor_->has_surface();
}
void DelegatedFrameHost::WasHidden(HiddenCause cause) {
tab_switch_time_recorder_.TabWasHidden();
#if BUILDFLAG(IS_WIN)
if (cause == HiddenCause::kOccluded)
return;
#endif
frame_evictor_->SetVisible(false);
}
void DelegatedFrameHost::CopyFromCompositingSurface(
const gfx::Rect& src_subrect,
const gfx::Size& output_size,
base::OnceCallback<void(const viz::CopyOutputBitmapWithMetadata&)>
callback) {
const viz::SurfaceId surface_id(frame_sink_id_, local_surface_id_);
ui::Compositor::ScopedKeepSurfaceAliveCallback keep_surface_alive;
if (compositor_ && CanCopyFromCompositingSurface()) {
keep_surface_alive =
compositor_->TakeScopedKeepSurfaceAliveCallback(surface_id);
}
CopyFromCompositingSurfaceInternal(
src_subrect, output_size, surface_id,
viz::CopyOutputRequest::ResultFormat::RGBA,
viz::CopyOutputRequest::ResultDestination::kSystemMemory,
base::BindOnce(
[](base::OnceCallback<void(const viz::CopyOutputBitmapWithMetadata&)>
callback,
ui::Compositor::ScopedKeepSurfaceAliveCallback keep_alive,
std::unique_ptr<viz::CopyOutputResult> result) {
if (keep_alive) {
std::move(keep_alive).RunAndReset();
}
auto scoped_bitmap = result->ScopedAccessSkBitmap();
std::move(callback).Run(
scoped_bitmap.GetOutScopedBitmapAndMetadata());
},
std::move(callback), std::move(keep_surface_alive)));
}
void DelegatedFrameHost::CopyFromCompositingSurfaceAsTexture(
const gfx::Rect& src_subrect,
const gfx::Size& output_size,
viz::CopyOutputRequest::CopyOutputRequestCallback callback) {
const viz::SurfaceId surface_id(frame_sink_id_, local_surface_id_);
CopyFromCompositingSurfaceInternal(
src_subrect, output_size, surface_id,
viz::CopyOutputRequest::ResultFormat::RGBA,
viz::CopyOutputRequest::ResultDestination::kSharedImage,
std::move(callback));
}
void DelegatedFrameHost::CopyFromCompositingSurfaceInternal(
const gfx::Rect& src_subrect,
const gfx::Size& output_size,
const viz::SurfaceId& surface_id,
viz::CopyOutputRequest::ResultFormat format,
viz::CopyOutputRequest::ResultDestination destination,
viz::CopyOutputRequest::CopyOutputRequestCallback callback) {
auto request = std::make_unique<viz::CopyOutputRequest>(format, destination,
std::move(callback));
request->set_result_task_runner(
base::SingleThreadTaskRunner::GetCurrentDefault());
if (!CanCopyFromCompositingSurface())
return;
if (!src_subrect.IsEmpty()) {
request->set_area(
gfx::ScaleToRoundedRect(src_subrect, client_->GetDeviceScaleFactor()));
}
if (!output_size.IsEmpty()) {
if (!request->has_area()) {
request->set_area(gfx::Rect(gfx::ScaleToRoundedSize(
surface_dip_size_, client_->GetDeviceScaleFactor())));
}
request->set_result_selection(gfx::Rect(output_size));
const gfx::Rect& area = request->area();
if (area.IsEmpty()) {
return;
}
request->SetScaleRatio(
gfx::Vector2d(area.width(), area.height()),
gfx::Vector2d(output_size.width(), output_size.height()));
}
CHECK(host_frame_sink_manager_);
host_frame_sink_manager_->RequestCopyOfOutput(surface_id, std::move(request));
}
void DelegatedFrameHost::SetFrameEvictionStateAndNotifyObservers(
FrameEvictionState frame_eviction_state) {
if (frame_eviction_state_ == frame_eviction_state)
return;
frame_eviction_state_ = frame_eviction_state;
for (auto& obs : observers_)
obs.OnFrameEvictionStateChanged(frame_eviction_state_);
}
bool DelegatedFrameHost::CanCopyFromCompositingSurface() const {
return local_surface_id_.is_valid();
}
bool DelegatedFrameHost::HasPrimarySurface() const {
const viz::SurfaceId* primary_surface_id =
client_->DelegatedFrameHostGetLayer()->GetSurfaceId();
return primary_surface_id && primary_surface_id->is_valid();
}
bool DelegatedFrameHost::HasFallbackSurface() const {
const viz::SurfaceId* fallback_surface_id =
client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
return fallback_surface_id && fallback_surface_id->is_valid();
}
viz::SurfaceId DelegatedFrameHost::GetFallbackSurfaceIdForTesting() const {
const viz::SurfaceId* fallback_surface_id =
client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
return fallback_surface_id ? *fallback_surface_id : viz::SurfaceId();
}
void DelegatedFrameHost::EmbedSurface(
const viz::LocalSurfaceId& new_local_surface_id,
const gfx::Size& new_dip_size,
cc::DeadlinePolicy deadline_policy) {
TRACE_EVENT2("viz", "DelegatedFrameHost::EmbedSurface", "surface_id",
new_local_surface_id.ToString(), "deadline_policy",
deadline_policy.ToString());
const viz::SurfaceId* primary_surface_id =
client_->DelegatedFrameHostGetLayer()->GetSurfaceId();
local_surface_id_ = new_local_surface_id;
surface_dip_size_ = new_dip_size;
pre_navigation_local_surface_id_ = viz::LocalSurfaceId();
if (!first_local_surface_id_after_navigation_.is_valid())
first_local_surface_id_after_navigation_ = local_surface_id_;
viz::SurfaceId new_primary_surface_id(frame_sink_id_, local_surface_id_);
if (!client_->DelegatedFrameHostIsVisible()) {
if (!current_frame_size_in_dip_.IsEmpty() &&
surface_dip_size_ != current_frame_size_in_dip_) {
client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
new_primary_surface_id);
bfcache_fallback_ =
viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId();
}
return;
}
if (!new_dip_size.IsEmpty())
frame_evictor_->OnNewSurfaceEmbedded();
if (bfcache_fallback_.is_valid()) {
deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u);
client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
viz::SurfaceId(frame_sink_id_, bfcache_fallback_));
bfcache_fallback_ =
viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId();
}
if (!primary_surface_id ||
primary_surface_id->local_surface_id() != local_surface_id_) {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
if (deadline_policy.policy_type() !=
cc::DeadlinePolicy::kUseInfiniteDeadline &&
!current_frame_size_in_dip_.IsEmpty() &&
current_frame_size_in_dip_ != surface_dip_size_) {
deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u);
}
#endif
current_frame_size_in_dip_ = surface_dip_size_;
client_->DelegatedFrameHostGetLayer()->SetShowSurface(
new_primary_surface_id, current_frame_size_in_dip_, GetGutterColor(),
deadline_policy, false );
if (compositor_)
compositor_->OnChildResizing();
}
}
SkColor DelegatedFrameHost::GetGutterColor() const {
return client_->DelegatedFrameHostGetGutterColor();
}
void DelegatedFrameHost::OnFirstSurfaceActivation(
const viz::SurfaceInfo& surface_info) {
NOTREACHED();
}
void DelegatedFrameHost::OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) {
client_->OnFrameTokenChanged(frame_token, activation_time);
}
#if BUILDFLAG(ARKWEB_MAXIMIZE_RESIZE)
void DelegatedFrameHost::RestoreRenderFit() {
if (client_) {
client_->RestoreRenderFit();
}
}
#endif
#if BUILDFLAG(ARKWEB_ROTATE_RESIZE)
void DelegatedFrameHost::ModifyRenderFit(int32_t fitType) {
if (client_) {
client_->ModifyRenderFit(fitType);
}
}
#endif
void DelegatedFrameHost::ClearFallbackSurfaceForCommitPending() {
const viz::SurfaceId* fallback_surface_id =
client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
if (fallback_surface_id && fallback_surface_id->is_valid()) {
EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction());
client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
viz::SurfaceId());
}
}
void DelegatedFrameHost::ResetFallbackToFirstNavigationSurface() {
const viz::SurfaceId* fallback_surface_id =
client_->DelegatedFrameHostGetLayer()->GetOldestAcceptableFallback();
if (fallback_surface_id &&
fallback_surface_id->frame_sink_id() == frame_sink_id_ &&
fallback_surface_id->local_surface_id().IsSameOrNewerThan(
first_local_surface_id_after_navigation_)) {
return;
}
if (pre_navigation_local_surface_id_.is_valid() &&
!first_local_surface_id_after_navigation_.is_valid()) {
CHECK(!bfcache_fallback_.is_valid());
EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction());
}
client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
first_local_surface_id_after_navigation_.is_valid()
? viz::SurfaceId(frame_sink_id_,
first_local_surface_id_after_navigation_)
: viz::SurfaceId());
}
void DelegatedFrameHost::EvictDelegatedFrame(
const std::vector<viz::SurfaceId>& surface_ids) {
if (frame_eviction_state_ == FrameEvictionState::kPendingEvictionRequests) {
frame_evictor_->OnSurfaceDiscarded();
return;
}
if (!HasSavedFrame()) {
ContinueDelegatedFrameEviction(surface_ids);
return;
}
if (client_->ShouldShowStaleContentOnEviction() &&
!stale_content_layer_->has_external_content()) {
SetFrameEvictionStateAndNotifyObservers(
FrameEvictionState::kPendingEvictionRequests);
auto callback =
base::BindOnce(&DelegatedFrameHost::DidCopyStaleContent, GetWeakPtr());
CopyFromCompositingSurfaceAsTexture(
gfx::Rect(),
gfx::ScaleToRoundedSize(surface_dip_size_, kFrameContentCaptureQuality),
std::move(callback));
} else {
ContinueDelegatedFrameEviction(surface_ids);
}
frame_evictor_->OnSurfaceDiscarded();
}
viz::FrameEvictorClient::EvictIds
DelegatedFrameHost::CollectSurfaceIdsForEviction() const {
return client_->CollectSurfaceIdsForEviction();
}
viz::SurfaceId DelegatedFrameHost::GetCurrentSurfaceId() const {
return viz::SurfaceId(frame_sink_id_, local_surface_id_);
}
viz::SurfaceId DelegatedFrameHost::GetPreNavigationSurfaceId() const {
return viz::SurfaceId(frame_sink_id_, pre_navigation_local_surface_id_);
}
void DelegatedFrameHost::DidCopyStaleContent(
std::unique_ptr<viz::CopyOutputResult> result) {
if (frame_evictor_->visible() || result->IsEmpty())
return;
CHECK_EQ(result->format(), viz::CopyOutputResult::Format::RGBA);
CHECK_EQ(result->destination(),
viz::CopyOutputResult::Destination::kSharedImage);
#if !BUILDFLAG(IS_CHROMEOS)
CHECK_NE(frame_eviction_state_, FrameEvictionState::kNotStarted);
#endif
SetFrameEvictionStateAndNotifyObservers(FrameEvictionState::kNotStarted);
ContinueDelegatedFrameEviction(
frame_evictor_->CollectSurfaceIdsForEviction());
auto transfer_resource = viz::TransferableResource::Make(
result->GetSharedImage(),
viz::TransferableResource::ResourceSource::kStaleContent,
gpu::SyncToken(), {.color_space = gfx::ColorSpace()});
viz::ReleaseCallback release_callback = result->TakeSharedImageOwnership();
CHECK(release_callback);
if (stale_content_layer_->parent() != client_->DelegatedFrameHostGetLayer())
client_->DelegatedFrameHostGetLayer()->Add(stale_content_layer_.get());
#if !BUILDFLAG(IS_CHROMEOS)
CHECK(!stale_content_layer_->has_external_content());
#endif
stale_content_layer_->SetVisible(true);
stale_content_layer_->SetBounds(gfx::Rect(surface_dip_size_));
stale_content_layer_->SetTransferableResource(
transfer_resource, std::move(release_callback), surface_dip_size_);
}
void DelegatedFrameHost::ContinueDelegatedFrameEviction(
const std::vector<viz::SurfaceId>& surface_ids) {
if (HasPrimarySurface()) {
client_->DelegatedFrameHostGetLayer()->SetShowSurface(
viz::SurfaceId(), current_frame_size_in_dip_, GetGutterColor(),
cc::DeadlinePolicy::UseDefaultDeadline(), false);
}
if (!HasSavedFrame())
return;
DCHECK(!local_surface_id_.is_valid() || !surface_ids.empty());
if (!surface_ids.empty()) {
CHECK(host_frame_sink_manager_);
host_frame_sink_manager_->EvictSurfaces(surface_ids);
}
client_->InvalidateLocalSurfaceIdOnEviction();
}
void DelegatedFrameHost::OnCompositingShuttingDown(ui::Compositor* compositor) {
CHECK_EQ(compositor, compositor_);
DetachFromCompositor();
CHECK(!compositor_);
}
void DelegatedFrameHost::OnFirstSurfaceActivation(
ui::Compositor* compositor,
const viz::SurfaceInfo& surface_info) {}
void DelegatedFrameHost::AttachToCompositor(ui::Compositor* compositor) {
CHECK(!compositor_);
if (!compositor)
return;
compositor_ = compositor;
compositor_->AddObserver(this);
if (should_register_frame_sink_id_)
compositor_->AddChildFrameSink(frame_sink_id_);
}
void DelegatedFrameHost::DetachFromCompositor() {
if (!compositor_)
return;
if (compositor_->HasObserver(this))
compositor_->RemoveObserver(this);
if (should_register_frame_sink_id_)
compositor_->RemoveChildFrameSink(frame_sink_id_);
compositor_ = nullptr;
}
void DelegatedFrameHost::DidNavigate() {
first_local_surface_id_after_navigation_ = local_surface_id_;
}
void DelegatedFrameHost::DidNavigateMainFramePreCommit() {
pre_navigation_local_surface_id_ = local_surface_id_;
first_local_surface_id_after_navigation_ = viz::LocalSurfaceId();
local_surface_id_ = viz::LocalSurfaceId();
if (bfcache_fallback_.is_valid()) {
SCOPED_CRASH_KEY_STRING64("crbug-356337182", "bfc_fallback_crashed",
bfcache_fallback_.ToString().c_str());
SCOPED_CRASH_KEY_STRING64(
"crbug-356337182", "pre_nav_lsid_crashed",
pre_navigation_local_surface_id_.ToString().c_str());
SCOPED_CRASH_KEY_STRING64("crbug-356337182", "current_lsid_crashed",
local_surface_id_.ToString().c_str());
base::debug::DumpWithoutCrashing();
bfcache_fallback_ = viz::LocalSurfaceId();
}
}
void DelegatedFrameHost::DidEnterBackForwardCache() {
if (local_surface_id_.is_valid()) {
CHECK(!pre_navigation_local_surface_id_.is_valid());
CHECK(!bfcache_fallback_.is_valid());
} else {
local_surface_id_ = pre_navigation_local_surface_id_;
bfcache_fallback_ = pre_navigation_local_surface_id_;
pre_navigation_local_surface_id_ = viz::LocalSurfaceId();
}
}
void DelegatedFrameHost::ActivatedOrEvictedFromBackForwardCache() {
bfcache_fallback_ = viz::LocalSurfaceId();
}
void DelegatedFrameHost::WindowTitleChanged(const std::string& title) {
if (host_frame_sink_manager_)
host_frame_sink_manager_->SetFrameSinkDebugLabel(frame_sink_id_, title);
}
void DelegatedFrameHost::TakeFallbackContentFrom(DelegatedFrameHost* other) {
if (!other->HasPrimarySurface())
return;
if (HasFallbackSurface())
return;
const viz::SurfaceId* other_primary =
other->client_->DelegatedFrameHostGetLayer()->GetSurfaceId();
const viz::SurfaceId* other_fallback =
other->client_->DelegatedFrameHostGetLayer()
->GetOldestAcceptableFallback();
viz::SurfaceId desired_fallback;
if (!other_fallback || !other_primary->IsSameOrNewerThan(*other_fallback)) {
desired_fallback = other_primary->ToSmallestId();
} else {
desired_fallback = *other_fallback;
}
bfcache_fallback_ =
viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId();
if (!HasPrimarySurface()) {
client_->DelegatedFrameHostGetLayer()->SetShowSurface(
desired_fallback, other->client_->DelegatedFrameHostGetLayer()->size(),
other->client_->DelegatedFrameHostGetLayer()->background_color(),
cc::DeadlinePolicy::UseDefaultDeadline(),
false );
}
client_->DelegatedFrameHostGetLayer()->SetOldestAcceptableFallback(
desired_fallback);
}
viz::SurfaceId DelegatedFrameHost::GetFirstSurfaceIdAfterNavigationForTesting()
const {
return viz::SurfaceId(frame_sink_id_,
first_local_surface_id_after_navigation_);
}
viz::SurfaceId DelegatedFrameHost::GetBFCacheFallbackSurfaceIdForTesting()
const {
return viz::SurfaceId(frame_sink_id_, bfcache_fallback_);
}
void DelegatedFrameHost::SetIsFrameSinkIdOwner(bool is_owner) {
if (is_owner == owns_frame_sink_id_) {
return;
}
owns_frame_sink_id_ = is_owner;
if (owns_frame_sink_id_) {
host_frame_sink_manager_->RegisterFrameSinkId(
frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
host_frame_sink_manager_->SetFrameSinkDebugLabel(frame_sink_id_,
"DelegatedFrameHost");
}
}
}