Don't trigger autoplay when capturing a thumbnail

Currently, capturing a thumbnail for a background tab must call
WebContents::IncrementCapturerCount(). However, this tells the renderer
that a page is visible, triggering any deferred media autoplay.

This adds a flag to IncrementCapturerCount() which makes it use the new
PageVisibilityState::kHiddenButPainting state. This indicates that the
page should be rendered, but shouldn't be treated as user-visible yet.

Fixed: 995131
Change-Id: Ie90fdc25e412969d904f93c8c5ffcbc2312ddc0b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1885511
Reviewed-by: Avi Drissman <[email protected]>
Reviewed-by: danakj <[email protected]>
Reviewed-by: Dana Fried <[email protected]>
Commit-Queue: Collin Baker <[email protected]>
Cr-Commit-Position: refs/heads/master@{#715314}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 8805f68a..c22a83d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -576,7 +576,8 @@
       is_resume_pending_(false),
       interstitial_page_(nullptr),
       has_accessed_initial_document_(false),
-      capturer_count_(0),
+      visible_capturer_count_(0),
+      hidden_capturer_count_(0),
       is_being_destroyed_(false),
       is_notifying_observers_(false),
       notify_disconnection_(false),
@@ -1448,12 +1449,13 @@
   GetFrameTree()->root()->set_was_discarded();
 }
 
-void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size) {
+void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size,
+                                             bool stay_hidden) {
   DCHECK(!is_being_destroyed_);
-  const bool was_captured = IsBeingCaptured();
-  ++capturer_count_;
-  DVLOG(1) << "There are now " << capturer_count_
-           << " capturing(s) of WebContentsImpl@" << this;
+  if (stay_hidden)
+    ++hidden_capturer_count_;
+  else
+    ++visible_capturer_count_;
 
   // Note: This provides a hint to upstream code to size the views optimally
   // for quality (e.g., to avoid scaling).
@@ -1462,15 +1464,16 @@
     OnPreferredSizeChanged(preferred_size_);
   }
 
-  if (!was_captured)
-    UpdateVisibilityAndNotifyPageAndView(GetVisibility());
+  UpdateVisibilityAndNotifyPageAndView(GetVisibility());
 }
 
-void WebContentsImpl::DecrementCapturerCount() {
-  --capturer_count_;
-  DVLOG(1) << "There are now " << capturer_count_
-           << " capturing(s) of WebContentsImpl@" << this;
-  DCHECK_LE(0, capturer_count_);
+void WebContentsImpl::DecrementCapturerCount(bool stay_hidden) {
+  if (stay_hidden)
+    --hidden_capturer_count_;
+  else
+    --visible_capturer_count_;
+  DCHECK_GE(hidden_capturer_count_, 0);
+  DCHECK_GE(visible_capturer_count_, 0);
 
   if (is_being_destroyed_)
     return;
@@ -1479,12 +1482,13 @@
     const gfx::Size old_size = preferred_size_for_capture_;
     preferred_size_for_capture_ = gfx::Size();
     OnPreferredSizeChanged(old_size);
-    UpdateVisibilityAndNotifyPageAndView(GetVisibility());
   }
+
+  UpdateVisibilityAndNotifyPageAndView(GetVisibility());
 }
 
 bool WebContentsImpl::IsBeingCaptured() {
-  return capturer_count_ > 0;
+  return visible_capturer_count_ + hidden_capturer_count_ > 0;
 }
 
 bool WebContentsImpl::IsAudioMuted() {
@@ -2564,14 +2568,22 @@
 void WebContentsImpl::UpdateVisibilityAndNotifyPageAndView(
     Visibility new_visibility) {
   // Only hide the page if there are no entities capturing screenshots
-  // or video (e.g. mirroring).
-  const bool page_is_visible =
-      new_visibility == Visibility::VISIBLE || IsBeingCaptured();
+  // or video (e.g. mirroring). If there are, apply the correct state of
+  // kHidden or kHiddenButPainting.
+  PageVisibilityState page_visibility;
+  if (new_visibility == Visibility::VISIBLE || visible_capturer_count_ > 0)
+    page_visibility = PageVisibilityState::kVisible;
+  else if (hidden_capturer_count_ > 0)
+    page_visibility = PageVisibilityState::kHiddenButPainting;
+  else
+    page_visibility = PageVisibilityState::kHidden;
   // If there are entities in Picture-in-Picture mode, don't activate
   // the "disable rendering" optimization.
-  const bool view_is_visible = page_is_visible || HasPictureInPictureVideo();
+  const bool view_is_visible =
+      page_visibility != PageVisibilityState::kHidden ||
+      HasPictureInPictureVideo();
 
-  if (page_is_visible) {
+  if (page_visibility != PageVisibilityState::kHidden) {
     // We cannot show a page or capture video unless there is a valid renderer
     // associated with this web contents. The navigation controller for this
     // page must be set to active (allowing navigation to complete, a renderer
@@ -2581,7 +2593,7 @@
     // Previously, it was possible for browser-side code to try to capture video
     // from a restored tab (for a variety of reasons, including the browser
     // creating preview thumbnails) and the tab would never actually load. By
-    // keying this behavior off of |page_is_visible| instead of just
+    // keying this behavior off of |page_visibility| instead of just
     // |new_visibility| we avoid this case. See crbug.com/1020782 for more
     // context.
     controller_.SetActive(true);
@@ -2591,8 +2603,8 @@
     // as soon as they are shown. But the Page and other classes do not expect
     // to be producing frames when the Page is hidden. So we make sure the Page
     // is shown first.
-    SendPageMessage(new PageMsg_VisibilityChanged(
-        MSG_ROUTING_NONE, PageVisibilityState::kVisible));
+    SendPageMessage(
+        new PageMsg_VisibilityChanged(MSG_ROUTING_NONE, page_visibility));
   }
 
   // |GetRenderWidgetHostView()| can be null if the user middle clicks a link to
@@ -2624,7 +2636,7 @@
     SetVisibilityAndNotifyObservers(new_visibility);
   }
 
-  if (!page_is_visible) {
+  if (page_visibility == PageVisibilityState::kHidden) {
     // Similar to when showing the page, we only hide the page after
     // hiding the individual RenderWidgets.
     SendPageMessage(new PageMsg_VisibilityChanged(