| // Copyright 2016 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ui/android/delegated_frame_host_android.h" | 
 |  | 
 | #include <iterator> | 
 |  | 
 | #include "base/android/android_info.h" | 
 | #include "base/check_op.h" | 
 | #include "base/debug/crash_logging.h" | 
 | #include "base/debug/dump_without_crashing.h" | 
 | #include "base/feature_list.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/scoped_refptr.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/notreached.h" | 
 | #include "base/time/time.h" | 
 | #include "base/trace_event/trace_event.h" | 
 | #include "cc/slim/layer.h" | 
 | #include "cc/slim/layer_tree.h" | 
 | #include "cc/slim/surface_layer.h" | 
 | #include "components/viz/common/features.h" | 
 | #include "components/viz/common/frame_sinks/blit_request.h" | 
 | #include "components/viz/common/frame_sinks/copy_output_result.h" | 
 | #include "components/viz/common/gpu/raster_context_provider.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_utils.h" | 
 | #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" | 
 | #include "components/viz/common/surfaces/surface_id.h" | 
 | #include "components/viz/common/viz_utils.h" | 
 | #include "components/viz/host/host_frame_sink_manager.h" | 
 | #include "gpu/command_buffer/client/client_shared_image.h" | 
 | #include "gpu/command_buffer/client/shared_image_interface.h" | 
 | #include "gpu/command_buffer/common/shared_image_usage.h" | 
 | #include "gpu/command_buffer/common/sync_token.h" | 
 | #include "ui/android/browser_controls_offset_tag_constraints.h" | 
 | #include "ui/android/browser_controls_offset_tag_definitions.h" | 
 | #include "ui/android/view_android.h" | 
 | #include "ui/android/window_android.h" | 
 | #include "ui/android/window_android_compositor.h" | 
 | #include "ui/display/display.h" | 
 | #include "ui/display/screen.h" | 
 | #include "ui/gfx/color_space.h" | 
 | #include "ui/gfx/geometry/dip_util.h" | 
 |  | 
 | namespace ui { | 
 |  | 
 | namespace { | 
 |  | 
 | scoped_refptr<cc::slim::SurfaceLayer> CreateSurfaceLayer( | 
 |     const viz::SurfaceId& primary_surface_id, | 
 |     const viz::SurfaceId& fallback_surface_id, | 
 |     const gfx::Size& size_in_pixels, | 
 |     const cc::DeadlinePolicy& deadline_policy, | 
 |     bool surface_opaque) { | 
 |   // manager must outlive compositors using it. | 
 |   auto layer = cc::slim::SurfaceLayer::Create(); | 
 |   layer->SetSurfaceId(primary_surface_id, deadline_policy); | 
 |   layer->SetOldestAcceptableFallback(fallback_surface_id); | 
 |   layer->SetBounds(size_in_pixels); | 
 |   layer->SetIsDrawable(true); | 
 |   layer->SetContentsOpaque(surface_opaque); | 
 |  | 
 |   return layer; | 
 | } | 
 |  | 
 | // From content::VisibleTimeRequestTrigger::ConsumeAndMergeRequests | 
 | // TODO(crbug.com/40203057): Use separate start time for each event. | 
 | blink::mojom::RecordContentToVisibleTimeRequestPtr ConsumeAndMergeRequests( | 
 |     blink::mojom::RecordContentToVisibleTimeRequestPtr request1, | 
 |     blink::mojom::RecordContentToVisibleTimeRequestPtr request2) { | 
 |   if (!request1 && !request2) | 
 |     return nullptr; | 
 |  | 
 |   // Pick any non-null request to merge into. | 
 |   blink::mojom::RecordContentToVisibleTimeRequestPtr to; | 
 |   blink::mojom::RecordContentToVisibleTimeRequestPtr from; | 
 |   if (request1) { | 
 |     to = std::move(request1); | 
 |     from = std::move(request2); | 
 |   } else { | 
 |     to = std::move(request2); | 
 |     from = std::move(request1); | 
 |   } | 
 |  | 
 |   if (from) { | 
 |     to->event_start_time = | 
 |         std::min(to->event_start_time, from->event_start_time); | 
 |     to->destination_is_loaded |= from->destination_is_loaded; | 
 |     to->show_reason_tab_switching |= from->show_reason_tab_switching; | 
 |     to->show_reason_bfcache_restore |= from->show_reason_bfcache_restore; | 
 |   } | 
 |   return to; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | DelegatedFrameHostAndroid::DelegatedFrameHostAndroid( | 
 |     ui::ViewAndroid* view, | 
 |     viz::HostFrameSinkManager* host_frame_sink_manager, | 
 |     Client* client, | 
 |     const viz::FrameSinkId& frame_sink_id) | 
 |     : frame_sink_id_(frame_sink_id), | 
 |       view_(view), | 
 |       host_frame_sink_manager_(host_frame_sink_manager), | 
 |       client_(client), | 
 |       frame_evictor_(std::make_unique<viz::FrameEvictor>(this)) { | 
 |   DCHECK(view_); | 
 |   DCHECK(client_); | 
 |  | 
 |   constexpr bool is_transparent = false; | 
 |   content_layer_ = CreateSurfaceLayer( | 
 |       viz::SurfaceId(), viz::SurfaceId(), gfx::Size(), | 
 |       cc::DeadlinePolicy::UseDefaultDeadline(), is_transparent); | 
 |   view_->GetLayer()->AddChild(content_layer_); | 
 | } | 
 |  | 
 | DelegatedFrameHostAndroid::~DelegatedFrameHostAndroid() { | 
 |   EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction()); | 
 |   DetachFromCompositor(); | 
 |   if (owns_frame_sink_id_) { | 
 |     host_frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_, this, {}); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::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_, "DelegatedFrameHostAndroid"); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::RegisterOffsetTags( | 
 |     const BrowserControlsOffsetTagDefinitions& tag_definitions) { | 
 |   const cc::BrowserControlsOffsetTags& tags = tag_definitions.tags; | 
 |   const BrowserControlsOffsetTagConstraints& constraints = | 
 |       tag_definitions.constraints; | 
 |  | 
 |   const viz::OffsetTag& bottom_controls_offset_tag = | 
 |       tags.bottom_controls_offset_tag; | 
 |   if (!bottom_controls_offset_tag.IsEmpty()) { | 
 |     content_layer_->RegisterOffsetTag(bottom_controls_offset_tag, | 
 |                                       constraints.bottom_controls_constraints); | 
 |   } | 
 |  | 
 |   // TOOD(peilinwang) Enforce that either both tags exist or are both empty | 
 |   // after the NoBrowserFramesWithAdditionalCaptures BCIV experiment ramps up. | 
 |   const viz::OffsetTag& top_controls_offset_tag = tags.top_controls_offset_tag; | 
 |   const viz::OffsetTag& content_offset_tag = tags.content_offset_tag; | 
 |   if (!top_controls_offset_tag.IsEmpty()) { | 
 |     CHECK(!content_offset_tag.IsEmpty()); | 
 |     content_layer_->RegisterOffsetTag(top_controls_offset_tag, | 
 |                                       constraints.top_controls_constraints); | 
 |   } | 
 |   if (!content_offset_tag.IsEmpty()) { | 
 |     content_layer_->RegisterOffsetTag(content_offset_tag, | 
 |                                       constraints.content_constraints); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::UnregisterOffsetTags( | 
 |     const cc::BrowserControlsOffsetTags& tags) { | 
 |   const viz::OffsetTag& top_controls_offset_tag = tags.top_controls_offset_tag; | 
 |   if (!top_controls_offset_tag.IsEmpty()) { | 
 |     content_layer_->UnregisterOffsetTag(top_controls_offset_tag); | 
 |   } | 
 |  | 
 |   const viz::OffsetTag& content_offset_tag = tags.content_offset_tag; | 
 |   if (!content_offset_tag.IsEmpty()) { | 
 |     content_layer_->UnregisterOffsetTag(content_offset_tag); | 
 |   } | 
 |  | 
 |   const viz::OffsetTag& bottom_controls_offset_tag = | 
 |       tags.bottom_controls_offset_tag; | 
 |   if (!bottom_controls_offset_tag.IsEmpty()) { | 
 |     content_layer_->UnregisterOffsetTag(bottom_controls_offset_tag); | 
 |   } | 
 | } | 
 |  | 
 | const viz::FrameSinkId& DelegatedFrameHostAndroid::GetFrameSinkId() const { | 
 |   return frame_sink_id_; | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::CopyFromCompositingSurface( | 
 |     const gfx::Rect& src_subrect, | 
 |     const gfx::Size& output_size, | 
 |     base::OnceCallback<void(const viz::CopyOutputBitmapWithMetadata&)> callback, | 
 |     bool capture_exact_surface_id, | 
 |     base::TimeDelta ipc_delay) { | 
 |   DCHECK(CanCopyFromCompositingSurface()); | 
 |  | 
 |   const viz::SurfaceId surface_id(frame_sink_id_, local_surface_id_); | 
 |  | 
 |   ui::WindowAndroidCompositor::ScopedKeepSurfaceAliveCallback | 
 |       keep_surface_alive; | 
 |   if (view_->GetWindowAndroid() && view_->GetWindowAndroid()->GetCompositor()) { | 
 |     keep_surface_alive = view_->GetWindowAndroid() | 
 |                              ->GetCompositor() | 
 |                              ->TakeScopedKeepSurfaceAliveCallback(surface_id); | 
 |   } | 
 |  | 
 |   std::unique_ptr<viz::CopyOutputRequest> request = | 
 |       std::make_unique<viz::CopyOutputRequest>( | 
 |           viz::CopyOutputRequest::ResultFormat::RGBA, | 
 |           viz::CopyOutputRequest::ResultDestination::kSystemMemory, | 
 |           base::BindOnce( | 
 |               [](base::OnceCallback<void( | 
 |                      const viz::CopyOutputBitmapWithMetadata&)> copy_result, | 
 |                  ui::WindowAndroidCompositor::ScopedKeepSurfaceAliveCallback | 
 |                      keep_alive, | 
 |                  std::unique_ptr<viz::CopyOutputResult> result) { | 
 |                 if (keep_alive) { | 
 |                   std::move(keep_alive).Run(); | 
 |                 } | 
 |                 auto scoped_bitmap = result->ScopedAccessSkBitmap(); | 
 |                 std::move(copy_result) | 
 |                     .Run(scoped_bitmap.GetOutScopedBitmapAndMetadata()); | 
 |               }, | 
 |               std::move(callback), std::move(keep_surface_alive))); | 
 |   request->set_send_result_delay(ipc_delay); | 
 |  | 
 |   // The callback must be executed on the UI thread. Since the result callback | 
 |   // can be dispatched on any thread by default, explicitly set the result task | 
 |   // runner to the current thread. | 
 |   request->set_result_task_runner( | 
 |       base::SequencedTaskRunner::GetCurrentDefault()); | 
 |  | 
 |   viz::SetCopyOutputRequestResultSize(request.get(), src_subrect, output_size, | 
 |                                       surface_size_in_pixels_); | 
 |  | 
 |   host_frame_sink_manager_->RequestCopyOfOutput(surface_id, std::move(request), | 
 |                                                 capture_exact_surface_id); | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const { | 
 |   return local_surface_id_.is_valid(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::CopySharedImageFromCompositingSurface( | 
 |     scoped_refptr<viz::RasterContextProvider> context_provider, | 
 |     const gfx::Rect& src_subrect, | 
 |     const gfx::Size& output_size, | 
 |     base::OnceCallback<void(scoped_refptr<gpu::ClientSharedImage>, | 
 |                             viz::ReleaseCallback)> callback, | 
 |     bool capture_exact_surface_id) { | 
 |   TRACE_EVENT( | 
 |       "ui", "DelegatedFrameHostAndroid::CopySharedImageFromCompositingSurface"); | 
 |   DCHECK(context_provider); | 
 |   DCHECK(CanCopyFromCompositingSurface()); | 
 |  | 
 |   auto* shared_image_interface = context_provider->SharedImageInterface(); | 
 |   CHECK(shared_image_interface); | 
 |  | 
 |   gfx::Size image_size = | 
 |       output_size.IsEmpty() ? surface_size_in_pixels_ : output_size; | 
 |  | 
 |   display::Display display = | 
 |       view_->GetWindowAndroid()->GetDisplayWithWindowColorSpace(); | 
 |   gfx::ColorSpace color_space = display.GetColorSpaces().GetOutputColorSpace( | 
 |       gfx::ContentColorUsage::kSRGB, /*needs_alpha=*/false); | 
 |  | 
 |   auto shared_image = shared_image_interface->CreateSharedImage( | 
 |       {viz::SinglePlaneFormat::kRGBA_8888, image_size, color_space, | 
 |        gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | | 
 |            gpu::SHARED_IMAGE_USAGE_RASTER_READ | | 
 |            gpu::SHARED_IMAGE_USAGE_DISPLAY_WRITE, | 
 |        "DFHACopyFromCompositingSurface"}, | 
 |       gpu::kNullSurfaceHandle); | 
 |   if (!shared_image) { | 
 |     LOG(WARNING) | 
 |         << "Could not create a shared image to copy from compositing surface"; | 
 |     std::move(callback).Run(nullptr, viz::ReleaseCallback()); | 
 |     return; | 
 |   } | 
 |   viz::ReleaseCallback release_callback = base::BindOnce( | 
 |       [](scoped_refptr<gpu::ClientSharedImage> shared_image, | 
 |          const gpu::SyncToken& sync_token, bool is_lost) { | 
 |         shared_image->UpdateDestructionSyncToken(sync_token); | 
 |       }, | 
 |       shared_image); | 
 |  | 
 |   const viz::SurfaceId surface_id(frame_sink_id_, local_surface_id_); | 
 |   ui::WindowAndroidCompositor::ScopedKeepSurfaceAliveCallback | 
 |       keep_surface_alive; | 
 |   if (view_->GetWindowAndroid() && view_->GetWindowAndroid()->GetCompositor()) { | 
 |     keep_surface_alive = view_->GetWindowAndroid() | 
 |                              ->GetCompositor() | 
 |                              ->TakeScopedKeepSurfaceAliveCallback(surface_id); | 
 |   } | 
 |  | 
 |   std::unique_ptr<viz::CopyOutputRequest> request = | 
 |       std::make_unique<viz::CopyOutputRequest>( | 
 |           viz::CopyOutputResult::Format::RGBA, | 
 |           viz::CopyOutputResult::Destination::kSharedImage, | 
 |           base::BindOnce( | 
 |               [](base::OnceCallback<void(scoped_refptr<gpu::ClientSharedImage>, | 
 |                                          viz::ReleaseCallback)> result_callback, | 
 |                  viz::ReleaseCallback release_callback, | 
 |                  ui::WindowAndroidCompositor::ScopedKeepSurfaceAliveCallback | 
 |                      keep_alive, | 
 |                  std::unique_ptr<viz::CopyOutputResult> result) { | 
 |                 if (keep_alive) { | 
 |                   std::move(keep_alive).Run(); | 
 |                 } | 
 |                 if (result->IsEmpty()) { | 
 |                   // Report a null shared image in case there was a failure. | 
 |                   std::move(result_callback) | 
 |                       .Run(nullptr, viz::ReleaseCallback()); | 
 |                   return; | 
 |                 } | 
 |                 std::move(result_callback) | 
 |                     .Run(result->GetSharedImage(), std::move(release_callback)); | 
 |               }, | 
 |               std::move(callback), std::move(release_callback), | 
 |               std::move(keep_surface_alive))); | 
 |  | 
 |   auto sync_token = shared_image_interface->GenVerifiedSyncToken(); | 
 |   viz::SetCopyOutputRequestResultSize(request.get(), src_subrect, output_size, | 
 |                                       surface_size_in_pixels_); | 
 |   if (!request->has_result_selection()) { | 
 |     request->set_result_selection(gfx::Rect(image_size)); | 
 |   } | 
 |   request->set_blit_request( | 
 |       viz::BlitRequest(gfx::Point(), viz::LetterboxingBehavior::kDoNotLetterbox, | 
 |                        std::move(shared_image), sync_token, | 
 |                        /*populates_gpu_memory_buffer=*/false)); | 
 |  | 
 |   // The callback must be executed on the UI thread. Since the result callback | 
 |   // can be dispatched on any thread by default, explicitly set the result task | 
 |   // runner to the current thread. | 
 |   request->set_result_task_runner( | 
 |       base::SequencedTaskRunner::GetCurrentDefault()); | 
 |  | 
 |   host_frame_sink_manager_->RequestCopyOfOutput(surface_id, std::move(request), | 
 |                                                 capture_exact_surface_id); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::EvictDelegatedFrame( | 
 |     const std::vector<viz::SurfaceId>& surface_ids) { | 
 |   content_layer_->SetSurfaceId(viz::SurfaceId(), | 
 |                                cc::DeadlinePolicy::UseDefaultDeadline()); | 
 |   // If we have a surface from before a navigation, evict it, regardless of | 
 |   // visibility state. | 
 |   // | 
 |   // TODO(crbug.com/40919347): Investigate why guarding the invalid | 
 |   // `pre_navigation_local_surface_id_` for Android only. | 
 |   if (!pre_navigation_local_surface_id_.is_valid() && | 
 |       (!HasSavedFrame() || frame_evictor_->visible())) { | 
 |     return; | 
 |   } | 
 |  | 
 |   if (surface_ids.empty()) | 
 |     return; | 
 |   host_frame_sink_manager_->EvictSurfaces(surface_ids); | 
 |   frame_evictor_->OnSurfaceDiscarded(); | 
 |   // When surface sync is on, this call will force |client_| to allocate a new | 
 |   // LocalSurfaceId which will be embedded the next time the tab is shown. When | 
 |   // surface sync is off, the renderer will always allocate a new LocalSurfaceId | 
 |   // when it becomes visible just in case the previous LocalSurfaceId is evicted | 
 |   // by the browser. | 
 |   client_->WasEvicted(); | 
 | } | 
 |  | 
 | viz::FrameEvictorClient::EvictIds | 
 | DelegatedFrameHostAndroid::CollectSurfaceIdsForEviction() const { | 
 |   viz::FrameEvictorClient::EvictIds ids; | 
 |   ids.embedded_ids = client_->CollectSurfaceIdsForEviction(); | 
 |   return ids; | 
 | } | 
 |  | 
 | viz::SurfaceId DelegatedFrameHostAndroid::GetCurrentSurfaceId() const { | 
 |   return viz::SurfaceId(frame_sink_id_, local_surface_id_); | 
 | } | 
 |  | 
 | viz::SurfaceId DelegatedFrameHostAndroid::GetPreNavigationSurfaceId() const { | 
 |   return viz::SurfaceId(frame_sink_id_, pre_navigation_local_surface_id_); | 
 | } | 
 |  | 
 | viz::SurfaceId DelegatedFrameHostAndroid::GetFallbackSurfaceIdForTesting() | 
 |     const { | 
 |   return content_layer_->oldest_acceptable_fallback().value_or( | 
 |       viz::SurfaceId()); | 
 | } | 
 |  | 
 | viz::SurfaceId DelegatedFrameHostAndroid::GetCurrentSurfaceIdForTesting() | 
 |     const { | 
 |   return GetCurrentSurfaceId(); | 
 | } | 
 |  | 
 | viz::SurfaceId | 
 | DelegatedFrameHostAndroid::GetFirstSurfaceIdAfterNavigationForTesting() const { | 
 |   return viz::SurfaceId(frame_sink_id_, | 
 |                         first_local_surface_id_after_navigation_); | 
 | } | 
 |  | 
 | viz::SurfaceId | 
 | DelegatedFrameHostAndroid::GetBFCacheFallbackSurfaceIdForTesting() const { | 
 |   return viz::SurfaceId(frame_sink_id_, bfcache_fallback_); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::ClearFallbackSurfaceForCommitPending() { | 
 |   const std::optional<viz::SurfaceId> fallback_surface_id = | 
 |       content_layer_->oldest_acceptable_fallback(); | 
 |  | 
 |   // CommitPending without a target for TakeFallbackContentFrom. Since we cannot | 
 |   // guarantee that Navigation will complete, evict our surfaces which are from | 
 |   // a previous Navigation. | 
 |   if (fallback_surface_id && fallback_surface_id->is_valid()) { | 
 |     EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction()); | 
 |     content_layer_->SetOldestAcceptableFallback(viz::SurfaceId()); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::ResetFallbackToFirstNavigationSurface() { | 
 |   // Don't update the fallback if it's already newer than the first id after | 
 |   // navigation. | 
 |   if (content_layer_->oldest_acceptable_fallback() && | 
 |       content_layer_->oldest_acceptable_fallback()->frame_sink_id() == | 
 |           frame_sink_id_ && | 
 |       content_layer_->oldest_acceptable_fallback() | 
 |           ->local_surface_id() | 
 |           .IsSameOrNewerThan(first_local_surface_id_after_navigation_)) { | 
 |     return; | 
 |   } | 
 |  | 
 |   // If we have a surface from before a navigation, evict it as well. | 
 |   if (pre_navigation_local_surface_id_.is_valid() && | 
 |       !first_local_surface_id_after_navigation_.is_valid()) { | 
 |     // If we have a valid `pre_navigation_local_surface_id_`, we must not be in | 
 |     // BFCache. | 
 |     CHECK(!bfcache_fallback_.is_valid()); | 
 |     EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction()); | 
 |     content_layer_->SetBackgroundColor(SkColors::kTransparent); | 
 |   } | 
 |  | 
 |   content_layer_->SetOldestAcceptableFallback( | 
 |       viz::SurfaceId(frame_sink_id_, first_local_surface_id_after_navigation_)); | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::HasDelegatedContent() const { | 
 |   return content_layer_->surface_id().is_valid(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::CompositorFrameSinkChanged() { | 
 |   EvictDelegatedFrame(frame_evictor_->CollectSurfaceIdsForEviction()); | 
 |   if (registered_parent_compositor_) | 
 |     AttachToCompositor(registered_parent_compositor_); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::AttachToCompositor( | 
 |     WindowAndroidCompositor* compositor) { | 
 |   if (registered_parent_compositor_) | 
 |     DetachFromCompositor(); | 
 |   compositor->AddFrameSubmissionObserver(client_); | 
 |   compositor->AddChildFrameSink(frame_sink_id_); | 
 |   registered_parent_compositor_ = compositor; | 
 |   if (content_to_visible_time_request_) { | 
 |     registered_parent_compositor_ | 
 |         ->PostRequestSuccessfulPresentationTimeForNextFrame( | 
 |             content_to_visible_time_recorder_.TabWasShown( | 
 |                 /*has_saved_frames=*/true, | 
 |                 std::move(content_to_visible_time_request_))); | 
 |   } | 
 |   // If we are visible and embedded, then update the surface keep alive for | 
 |   // the newly attached compositor. | 
 |   if (frame_evictor_->visible()) { | 
 |     UpdateCaptureKeepAlive(); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::DetachFromCompositor() { | 
 |   if (!registered_parent_compositor_) | 
 |     return; | 
 |   ReleaseCaptureKeepAlive(); | 
 |   registered_parent_compositor_->RemoveFrameSubmissionObserver(client_); | 
 |   registered_parent_compositor_->RemoveChildFrameSink(frame_sink_id_); | 
 |   registered_parent_compositor_ = nullptr; | 
 |   content_to_visible_time_request_ = nullptr; | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::IsPrimarySurfaceEvicted() const { | 
 |   return !content_layer_->surface_id().is_valid(); | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::HasSavedFrame() const { | 
 |   return frame_evictor_->has_surface(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::WasHidden() { | 
 |   CancelSuccessfulPresentationTimeRequest(); | 
 |   frame_evictor_->SetVisible(false); | 
 |   ReleaseCaptureKeepAlive(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::WasShown( | 
 |     const viz::LocalSurfaceId& new_local_surface_id, | 
 |     const gfx::Size& new_size_in_pixels, | 
 |     bool is_fullscreen, | 
 |     blink::mojom::RecordContentToVisibleTimeRequestPtr | 
 |         content_to_visible_time_request) { | 
 |   if (content_to_visible_time_request) { | 
 |     PostRequestSuccessfulPresentationTimeForNextFrame( | 
 |         std::move(content_to_visible_time_request)); | 
 |   } | 
 |   frame_evictor_->SetVisible(true); | 
 |  | 
 |   EmbedSurface( | 
 |       new_local_surface_id, new_size_in_pixels, | 
 |       cc::DeadlinePolicy::UseSpecifiedDeadline(FirstFrameTimeoutFrames()), | 
 |       is_fullscreen); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::EmbedSurface( | 
 |     const viz::LocalSurfaceId& new_local_surface_id, | 
 |     const gfx::Size& new_size_in_pixels, | 
 |     cc::DeadlinePolicy deadline_policy, | 
 |     bool is_fullscreen) { | 
 |   TRACE_EVENT2("viz", "DelegatedFrameHostAndroid::EmbedSurface", "surface_id", | 
 |                new_local_surface_id.ToString(), "deadline_policy", | 
 |                deadline_policy.ToString()); | 
 |  | 
 |   // We should never attempt to embed an invalid surface. Catch this here to | 
 |   // track down the root cause. Otherwise we will have vague crashes later on | 
 |   // at serialization time. | 
 |   CHECK(new_local_surface_id.is_valid()); | 
 |  | 
 |   // Confirm that there is a valid fallback surface on, otherwise we need to | 
 |   // adjust deadline times. To avoid displaying invalid content. | 
 |   bool has_fallback_surface = | 
 |       (content_layer_->oldest_acceptable_fallback() && | 
 |        content_layer_->oldest_acceptable_fallback()->is_valid()); | 
 |   SetLocalSurfaceId(new_local_surface_id); | 
 |   // The embedding of a new surface completes the navigation process. | 
 |   pre_navigation_local_surface_id_ = viz::LocalSurfaceId(); | 
 |   // Navigations performed while hidden delay embedding until transitioning to | 
 |   // becoming visible. So we may not have a valid surace when DidNavigate is | 
 |   // called. Cache the first surface here so we have the correct oldest surface | 
 |   // to fallback to. | 
 |   if (!first_local_surface_id_after_navigation_.is_valid()) | 
 |     first_local_surface_id_after_navigation_ = local_surface_id_; | 
 |   surface_size_in_pixels_ = new_size_in_pixels; | 
 |  | 
 |   viz::SurfaceId current_primary_surface_id = content_layer_->surface_id(); | 
 |   viz::SurfaceId new_primary_surface_id(frame_sink_id_, local_surface_id_); | 
 |  | 
 |   if (!frame_evictor_->visible() || is_fullscreen) { | 
 |     // For fullscreen or when tab is hidden  we don't want to display old sized | 
 |     // content. So we advance the fallback forcing viz to fallback to blank | 
 |     // screen if renderer won't submit frame in time. See | 
 |     // https://crbug.com/1088369 and  https://crbug.com/813157 | 
 |     // | 
 |     // An empty content layer bounds indicates this renderer has never been made | 
 |     // visible. This is the case for pre-rendered contents. Don't use the | 
 |     // primary id as fallback since it's guaranteed to have no content. See | 
 |     // crbug.com/1218238. | 
 |     if (!content_layer_->bounds().IsEmpty() && | 
 |         surface_size_in_pixels_ != content_layer_->bounds() && | 
 |         (has_fallback_surface || bfcache_fallback_.is_valid())) { | 
 |       content_layer_->SetOldestAcceptableFallback(new_primary_surface_id); | 
 |       // We default to black background for fullscreen case. | 
 |       content_layer_->SetBackgroundColor( | 
 |           is_fullscreen ? SkColors::kBlack : SkColors::kTransparent); | 
 |  | 
 |       // Invalidates `bfcache_fallback_`, resize-while-hidden has given us the | 
 |       // latest `local_surface_id_`. | 
 |       bfcache_fallback_ = | 
 |           viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId(); | 
 |     } | 
 |   } | 
 |  | 
 |   if (!frame_evictor_->visible()) { | 
 |     // Don't update the SurfaceLayer when invisible to avoid blocking on | 
 |     // renderers that do not submit CompositorFrames. Next time the renderer | 
 |     // is visible, EmbedSurface will be called again. See WasShown. | 
 |     return; | 
 |   } | 
 |  | 
 |   frame_evictor_->OnNewSurfaceEmbedded(); | 
 |  | 
 |   if (bfcache_fallback_.is_valid()) { | 
 |     // Inform Viz to show the primary surface with new ID asap; if the new | 
 |     // surface isn't ready, use the fallback. | 
 |     deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u); | 
 |     content_layer_->SetOldestAcceptableFallback( | 
 |         viz::SurfaceId(frame_sink_id_, bfcache_fallback_)); | 
 |     bfcache_fallback_ = | 
 |         viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId(); | 
 |   } | 
 |  | 
 |   if (!current_primary_surface_id.is_valid() || | 
 |       current_primary_surface_id.local_surface_id() != local_surface_id_) { | 
 |     if (base::android::android_info::sdk_int() < | 
 |         base::android::android_info::SDK_VERSION_OREO) { | 
 |       // On version of Android earlier than Oreo, we would like to produce new | 
 |       // content as soon as possible or the OS will create an additional black | 
 |       // gutter. We only reset the deadline on the first frame (no bounds yet | 
 |       // specified) or on resize, and only if the deadline policy is not | 
 |       // infinite. | 
 |       if (deadline_policy.policy_type() != | 
 |               cc::DeadlinePolicy::kUseInfiniteDeadline && | 
 |           (content_layer_->bounds().IsEmpty() || | 
 |            content_layer_->bounds() != surface_size_in_pixels_)) { | 
 |         deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u); | 
 |       } | 
 |     } | 
 |     // If there is not a valid current surface, nor a valid fallback, we want to | 
 |     // produce new content as soon as possible. To avoid displaying invalide | 
 |     // content, such as surfaces from before a navigation. | 
 |     if (!has_fallback_surface) | 
 |       deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u); | 
 |     content_layer_->SetSurfaceId(new_primary_surface_id, deadline_policy); | 
 |     content_layer_->SetBounds(new_size_in_pixels); | 
 |   } | 
 |  | 
 |   // If DFHA is shown, make sure that the surface is kept alive. This is | 
 |   // required for e.g. tab sharing capture to work. | 
 |   UpdateCaptureKeepAlive(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::RequestSuccessfulPresentationTimeForNextFrame( | 
 |     blink::mojom::RecordContentToVisibleTimeRequestPtr | 
 |         content_to_content_to_visible_time_request) { | 
 |   PostRequestSuccessfulPresentationTimeForNextFrame( | 
 |       std::move(content_to_content_to_visible_time_request)); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::CancelSuccessfulPresentationTimeRequest() { | 
 |   content_to_visible_time_request_.reset(); | 
 |   content_to_visible_time_recorder_.TabWasHidden(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::OnFirstSurfaceActivation( | 
 |     const viz::SurfaceInfo& surface_info) { | 
 |   NOTREACHED(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::OnFrameTokenChanged( | 
 |     uint32_t frame_token, | 
 |     base::TimeTicks activation_time) { | 
 |   client_->OnFrameTokenChanged(frame_token, activation_time); | 
 | } | 
 |  | 
 | viz::SurfaceId DelegatedFrameHostAndroid::SurfaceId() const { | 
 |   return viz::SurfaceId(frame_sink_id_, local_surface_id_); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::SetLocalSurfaceId( | 
 |     const viz::LocalSurfaceId& local_surface_id) { | 
 |   local_surface_id_ = local_surface_id; | 
 |   client_->OnSurfaceIdChanged(); | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::HasPrimarySurface() const { | 
 |   return content_layer_->surface_id().is_valid(); | 
 | } | 
 |  | 
 | bool DelegatedFrameHostAndroid::HasFallbackSurface() const { | 
 |   return content_layer_->oldest_acceptable_fallback() && | 
 |          content_layer_->oldest_acceptable_fallback()->is_valid(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::TakeFallbackContentFrom( | 
 |     DelegatedFrameHostAndroid* other) { | 
 |   if (HasFallbackSurface() || !other->HasPrimarySurface()) | 
 |     return; | 
 |  | 
 |   // If we explicitly tell a BFCached View and its `DelegatedFrameHostAndroid` | 
 |   // to use a specific fallback, discard the preserved fallback for BFCache. | 
 |   // During the BFCache activation (`EmbedSurface`) we will be using the primary | 
 |   // surface's smallest ID as the fallback. | 
 |   bfcache_fallback_ = | 
 |       viz::ParentLocalSurfaceIdAllocator::InvalidLocalSurfaceId(); | 
 |  | 
 |   // TODO(crbug.com/40278354): Investigate why on Android we use the | 
 |   // primary ID unconditionally, which is different on `DelegatedFrameHost`. | 
 |   content_layer_->SetOldestAcceptableFallback( | 
 |       other->content_layer_->surface_id().ToSmallestId()); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::DidNavigate() { | 
 |   first_local_surface_id_after_navigation_ = local_surface_id_; | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::DidNavigateMainFramePreCommit() { | 
 |   // We are navigating to a different page, so the current |local_surface_id_| | 
 |   // and the fallback option of |first_local_surface_id_after_navigation_| are | 
 |   // no longer valid, as they represent older content from a different source. | 
 |   // | 
 |   // Cache the current |local_surface_id_| so that if navigation fails we can | 
 |   // evict it when transitioning to becoming visible. | 
 |   // | 
 |   // If the current page enters BFCache, `pre_navigation_local_surface_id_` will | 
 |   // be restored as the primary `LocalSurfaceId` for this | 
 |   // `DelegatedFrameHostAndroid`. | 
 |   pre_navigation_local_surface_id_ = local_surface_id_; | 
 |   first_local_surface_id_after_navigation_ = viz::LocalSurfaceId(); | 
 |   SetLocalSurfaceId(viz::LocalSurfaceId()); | 
 |  | 
 |   // The page is either activated or evicted from BFCache without notifying the | 
 |   // DelegatedFrameHost. In either cases, `bfcache_fallback_` must be | 
 |   // invalidated. | 
 |   // | 
 |   // TODO(https://crbug.com/356337182): Remove the DumpWithoutCrashing when the | 
 |   // bug is fixed. | 
 |   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 DelegatedFrameHostAndroid::DidEnterBackForwardCache() { | 
 |   if (local_surface_id_.is_valid()) { | 
 |     // `EmbedSurface` can be called after `DidNavigateMainFramePreCommit` and | 
 |     // before `DidEnterBackForwardCache`. This can happen if there is an | 
 |     // on-going Hi-DPI capture on the old frame (see | 
 |     // `WebContentsFrameTracker::RenderFrameHostChanged()`). | 
 |     // | 
 |     // The `EmbedSurface` will invalidate `pre_navigation_local_surface_id_`. In | 
 |     // this case we shouldn't restore the `local_surface_id_` nor | 
 |     // `bfcache_fallback_`because the surface should embed the latest | 
 |     // `local_surface_id_`. | 
 |     CHECK(!pre_navigation_local_surface_id_.is_valid()); | 
 |     CHECK(!bfcache_fallback_.is_valid()); | 
 |   } else { | 
 |     SetLocalSurfaceId(pre_navigation_local_surface_id_); | 
 |     bfcache_fallback_ = pre_navigation_local_surface_id_; | 
 |     pre_navigation_local_surface_id_ = viz::LocalSurfaceId(); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::ActivatedOrEvictedFromBackForwardCache() { | 
 |   bfcache_fallback_ = viz::LocalSurfaceId(); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid:: | 
 |     PostRequestSuccessfulPresentationTimeForNextFrame( | 
 |         blink::mojom::RecordContentToVisibleTimeRequestPtr | 
 |             content_to_visible_time_request) { | 
 |   // Since we could receive multiple requests while awaiting | 
 |   // `registered_parent_compositor_` we merge them. | 
 |   auto request = | 
 |       ConsumeAndMergeRequests(std::move(content_to_visible_time_request_), | 
 |                               std::move(content_to_visible_time_request)); | 
 |  | 
 |   if (!registered_parent_compositor_) { | 
 |     content_to_visible_time_request_ = std::move(request); | 
 |     return; | 
 |   } | 
 |  | 
 |   registered_parent_compositor_ | 
 |       ->PostRequestSuccessfulPresentationTimeForNextFrame( | 
 |           content_to_visible_time_recorder_.TabWasShown( | 
 |               /*has_saved_frames=*/true, std::move(request))); | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::UpdateCaptureKeepAlive() { | 
 |   if (!registered_parent_compositor_) { | 
 |     return; | 
 |   } | 
 |   if (capture_keep_alive_callback_) { | 
 |     std::move(capture_keep_alive_callback_).Run(); | 
 |   } | 
 |   auto surface_id = GetCurrentSurfaceId(); | 
 |   if (surface_id.is_valid()) { | 
 |     capture_keep_alive_callback_ = | 
 |         registered_parent_compositor_->TakeScopedKeepSurfaceAliveCallback( | 
 |             surface_id); | 
 |   } | 
 | } | 
 |  | 
 | void DelegatedFrameHostAndroid::ReleaseCaptureKeepAlive() { | 
 |   if (capture_keep_alive_callback_) { | 
 |     std::move(capture_keep_alive_callback_).Run(); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace ui |