chromium/content/browser/media/capture/web_contents_video_capture_device_browsertest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/media/capture/web_contents_video_capture_device.h"

#include <optional>
#include <tuple>

#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/test/pixel_test_utils.h"
#include "content/browser/media/capture/content_capture_device_browsertest_base.h"
#include "content/browser/media/capture/fake_video_capture_stack.h"
#include "content/browser/media/capture/frame_test_util.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/shell/browser/shell.h"
#include "media/base/media_switches.h"
#include "media/base/video_frame.h"
#include "media/base/video_types.h"
#include "media/base/video_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gl/gl_switches.h"

#if BUILDFLAG(IS_WIN)
#include "ui/aura/test/aura_test_utils.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ui_base_features.h"
#endif

namespace content {
namespace {

class WebContentsVideoCaptureDeviceBrowserTest
    : public ContentCaptureDeviceBrowserTestBase,
      public FrameTestUtil {};

// Tests that the device refuses to start if the WebContents target was
// destroyed before the device could start.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
#if defined(MEMORY_SANITIZER)
#define MAYBE_ErrorsOutIfWebContentsHasGoneBeforeDeviceStart
#else
#define MAYBE_ErrorsOutIfWebContentsHasGoneBeforeDeviceStart
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_ErrorsOutIfWebContentsHasGoneBeforeDeviceStart) {}

// Tests that the device starts, captures a frame, and then gracefully
// errors-out because the WebContents is destroyed before the device is stopped.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug.com/328658521): It is also flaky on macOS.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_ErrorsOutWhenWebContentsIsDestroyed
#else
#define MAYBE_ErrorsOutWhenWebContentsIsDestroyed
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_ErrorsOutWhenWebContentsIsDestroyed) {}

// Tests that capture is re-targetted when the render view of a WebContents
// changes.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug.com/328658521): It is also flaky on macOS.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_ChangesTargettedRenderView
#else
#define MAYBE_ChangesTargettedRenderView
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_ChangesTargettedRenderView) {}

#if BUILDFLAG(IS_WIN)
class WebContentsVideoCaptureDeviceBrowserTestAura
    : public WebContentsVideoCaptureDeviceBrowserTest {
 public:
  // WebContentsVideoCaptureDeviceBrowserTest:
  void SetUp() override {
    scoped_feature_list_.InitAndEnableFeatureWithParameters(
        features::kApplyNativeOcclusionToCompositor,
        {{features::kApplyNativeOcclusionToCompositorType.name,
          features::kApplyNativeOcclusionToCompositorTypeRelease}});

    WebContentsVideoCaptureDeviceBrowserTest::SetUp();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Verifies capture still works if the WindowTreeHost is occluded.
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTestAura,
                       CapturesWhenOccluded) {
  aura::WindowTreeHost* window_tree_host = shell()->window()->GetHost();
  aura::test::DisableNativeWindowOcclusionTracking(window_tree_host);
  NavigateToInitialDocument();
  AllocateAndStartAndWaitForFirstFrame();
  EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured());

  // Make a content change in the first page and wait for capture to reflect
  // that.
  ChangePageContentColor(SK_ColorRED);
  WaitForFrameWithColor(SK_ColorRED);

  // Simulate the WindowTreeHost being occluded.
  window_tree_host->SetNativeWindowOcclusionState(
      aura::Window::OcclusionState::OCCLUDED, {});

  EXPECT_TRUE(shell()->web_contents()->IsBeingCaptured());

  // Make a change and ensure it was captured.
  ChangePageContentColor(SK_ColorGREEN);
  WaitForFrameWithColor(SK_ColorGREEN);
}
#endif

// Tests that capture is re-targetted when a renderer crash is followed by a
// reload. Regression test for http://crbug.com/916332.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug.com/328658521): It is also flaky on macOS.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_RecoversAfterRendererCrash
#else
#define MAYBE_RecoversAfterRendererCrash
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_RecoversAfterRendererCrash) {}

// Tests that the device stops delivering frames while suspended. When resumed,
// any content changes that occurred during the suspend should cause a new frame
// to be delivered, to ensure the client is up-to-date.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug/328419809): Also flaky on Mac.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_SuspendsAndResumes
#else
#define MAYBE_SuspendsAndResumes
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_SuspendsAndResumes) {}

// Tests that the device delivers refresh frames when asked, while the source
// content is not changing.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug.com/328658521): It is also flaky on macOS.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_DeliversRefreshFramesUponRequest
#else
#define MAYBE_DeliversRefreshFramesUponRequest
#endif
IN_PROC_BROWSER_TEST_F(WebContentsVideoCaptureDeviceBrowserTest,
                       MAYBE_DeliversRefreshFramesUponRequest) {}

class WebContentsVideoCaptureDeviceBrowserTestP
    : public WebContentsVideoCaptureDeviceBrowserTest,
      public testing::WithParamInterface<
          std::tuple<bool, bool, bool, media::VideoPixelFormat>> {};

#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(
    All,
    WebContentsVideoCaptureDeviceBrowserTestP,
    testing::Combine(
        // Note: On ChromeOS and Android, software compositing is not an option.
        testing::Values(false /* GPU-accelerated compositing */),
        testing::Values(false /* variable aspect ratio */,
                        true /* fixed aspect ratio */),
        testing::Values(false /* page has only a main frame */,
                        true /* page contains a cross-site iframe */),
        testing::Values(media::VideoPixelFormat::PIXEL_FORMAT_I420)),
    &WebContentsVideoCaptureDeviceBrowserTestP::GetDescription);
#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
// On MacOS, there is a newly added support for NV12-in-GMB. It relies on GPU
// acceleration, but has a feature detection built-in if the format is
// specified as media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN.
INSTANTIATE_TEST_SUITE_P(
    All,
    WebContentsVideoCaptureDeviceBrowserTestP,
    testing::Combine(
        testing::Values(false /* GPU-accelerated compositing */,
                        true /* software compositing */),
        testing::Values(false /* variable aspect ratio */,
                        true /* fixed aspect ratio */),
        testing::Values(false /* page has only a main frame */,
                        true /* page contains a cross-site iframe */),
        testing::Values(media::VideoPixelFormat::PIXEL_FORMAT_I420,
                        media::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN)),
    &WebContentsVideoCaptureDeviceBrowserTestP::GetDescription);
#else
INSTANTIATE_TEST_SUITE_P();
#endif

// Tests that the device successfully captures a series of content changes,
// whether the browser is running with software compositing or GPU-accelerated
// compositing, whether the WebContents is visible/hidden or occluded/unoccluded
// and whether the main document contains a cross-site iframe.
// TODO(crbug.com/40947039): Fails with MSAN. Determine if enabling the test for
// MSAN is feasible or not
// TODO(crbug/328419809): Also flaky on Mac.
// TODO(crbug/329654821): Also flaky for ChromeOS ASAN LSAN and debug.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
    (BUILDFLAG(IS_CHROMEOS) && defined(ADDRESS_SANITIZER)) ||                \
    (BUILDFLAG(IS_CHROMEOS_ASH) && !defined(NDEBUG))
#define MAYBE_CapturesContentChanges
#else
#define MAYBE_CapturesContentChanges
#endif
IN_PROC_BROWSER_TEST_P(WebContentsVideoCaptureDeviceBrowserTestP,
                       MAYBE_CapturesContentChanges) {}

}  // namespace
}  // namespace content