Avoid never-ending loop in FrameEvictionManager::CullUnlockedFrames
If enable surface synchronization is disabled and the frame is
visible then DelegatedFrameHostAndroid::EvictDelegatedFrame()
will only reset content_layer_ to null but not evict the
surface as that causes https://crbug.com/933374.
Trouble is that if the frame is then hidden without any new
content_layer being created the next call to
DelegatedFrameHostAndroid::EvictDelegatedFrame() will do nothing
as content_layer_ is null but the frame is still in the list of
unlocked frames in FrameEvicitionManager causing the next
call to FrameEvictionManager::CullUnlockedFrames that tries to evict
the frame to do nothing and loop forever (or hit a DCHECK if those
are enabled).
Fix this by not doing early exit if content_layer_ is null as this
is not directly connected to if there is a frame to evict.
Bug: 959914
Change-Id: I3f83036d9a5ae2c734dfca720115d1faf4278c87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1596571
Reviewed-by: Eric Karl <ericrk@chromium.org>
Commit-Queue: Joel Klinghed <the_jk@opera.com>
Cr-Commit-Position: refs/heads/master@{#657483}
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 5798985..32c726f 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -188,13 +188,13 @@
}
void DelegatedFrameHostAndroid::EvictDelegatedFrame() {
- if (!content_layer_)
- return;
- content_layer_->SetSurfaceId(viz::SurfaceId(),
- cc::DeadlinePolicy::UseDefaultDeadline());
- if (!enable_surface_synchronization_) {
- content_layer_->RemoveFromParent();
- content_layer_ = nullptr;
+ if (content_layer_) {
+ content_layer_->SetSurfaceId(viz::SurfaceId(),
+ cc::DeadlinePolicy::UseDefaultDeadline());
+ if (!enable_surface_synchronization_) {
+ content_layer_->RemoveFromParent();
+ content_layer_ = nullptr;
+ }
}
if (!HasSavedFrame() || frame_evictor_->visible())
return;
diff --git a/ui/android/delegated_frame_host_android_unittest.cc b/ui/android/delegated_frame_host_android_unittest.cc
index 4ac6e360..a9985d4 100644
--- a/ui/android/delegated_frame_host_android_unittest.cc
+++ b/ui/android/delegated_frame_host_android_unittest.cc
@@ -415,5 +415,24 @@
EXPECT_FALSE(request->has_result_selection());
}
+TEST_F(DelegatedFrameHostAndroidLegacyNonVizTest, EvictWhileVisible) {
+ // Create a frame and mark it visible.
+ gfx::Size size(10, 10);
+ SetUpValidFrame(size);
+ auto id = allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
+ frame_host_->WasShown(id, size);
+ EXPECT_TRUE(frame_host_->HasSavedFrame());
+
+ // Evict, which should do nothing as the frame is still visible.
+ frame_host_->EvictDelegatedFrame();
+ EXPECT_TRUE(frame_host_->HasSavedFrame());
+
+ // Hide frame and evict again, now it should release the frame.
+ EXPECT_CALL(client_, WasEvicted());
+ frame_host_->WasHidden();
+ frame_host_->EvictDelegatedFrame();
+ EXPECT_FALSE(frame_host_->HasSavedFrame());
+}
+
} // namespace
} // namespace ui