chromium/content/browser/media/web_app_system_media_controls_browsertest.cc

// Copyright 2023 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/web_app_system_media_controls.h"

#include <optional>

#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread_win.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/media/media_keys_listener_manager_impl.h"
#include "content/browser/media/session/media_session_impl.h"
#include "content/browser/media/web_app_system_media_controls_manager.h"
#include "content/public/common/content_features.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/media_start_stop_observer.h"
#include "content/shell/browser/shell.h"
#include "media/base/media_switches.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace content {

// This test suite tests playing media in a content window and verifies control
// via system media controls controls the expected window. As instanced system
// media controls is developed under kWebAppSystemMediaControls.

// Currently, this test suite only runs on windows.
class WebAppSystemMediaControlsBrowserTest
    : public ContentBrowserTest,
      public WebAppSystemMediaControlsManagerObserver,
      public MediaKeysListenerManagerImplTestObserver {
 public:
  WebAppSystemMediaControlsBrowserTest() = default;

  WebAppSystemMediaControlsBrowserTest(
      const WebAppSystemMediaControlsBrowserTest&) = delete;
  WebAppSystemMediaControlsBrowserTest& operator=(
      const WebAppSystemMediaControlsBrowserTest&) = delete;

  ~WebAppSystemMediaControlsBrowserTest() override = default;

  void SetUpOnMainThread() override {
    // Start an HTTPS server that will serve files in from "content/test/data".
    https_server_ = std::make_unique<net::EmbeddedTestServer>(
        net::EmbeddedTestServer::TYPE_HTTPS);
    https_server_->ServeFilesFromSourceDirectory("content/test/data");
    ASSERT_TRUE(https_server_->Start());

    // Also start listening to events from a few different classes.
    MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
        BrowserMainLoop::GetInstance()->media_keys_listener_manager();
    media_keys_listener_manager_impl->SetObserverForTesting(this);

    WebAppSystemMediaControlsManager* web_app_system_media_controls_manager =
        media_keys_listener_manager_impl->web_app_system_media_controls_manager_
            .get();
    web_app_system_media_controls_manager->SetObserverForTesting(this);

    // Tests may want to utilize this runloop to detect when browser has been
    // bookkeeping added. We need to create this runloop early enough so that
    // the runloop always wins the race between the waiter asking to "wait for
    // browser added" and the browser actually being added.
    browser_added_run_loop_.emplace();

    // Do a similar thing for the web app added run loop.
    web_app_added_run_loop_.emplace();

    // And finally a similar thing for the watching media key run loop.
    start_watching_media_key_run_loop_.emplace();
  }

  void TearDownOnMainThread() override {
    system_media_controls::SystemMediaControls::
        SetVisibilityChangedCallbackForTesting(nullptr);
    ContentBrowserTest::TearDownOnMainThread();
  }

  net::EmbeddedTestServer* https_server() { return https_server_.get(); }

  void StartPlaybackAndWait(Shell* shell, const std::string& id) {
    shell->web_contents()->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
        base::ASCIIToUTF16(
            JsReplace("document.getElementById($1).play();", id)),
        base::NullCallback(), ISOLATED_WORLD_ID_GLOBAL);
    WaitForStart(shell);
  }

  void WaitForStart(Shell* shell) {
    MediaStartStopObserver observer(shell->web_contents(),
                                    MediaStartStopObserver::Type::kStart);
    observer.Wait();
  }

  void WaitForStop(Shell* shell) {
    MediaStartStopObserver observer(shell->web_contents(),
                                    MediaStartStopObserver::Type::kStop);
    observer.Wait();
  }

  bool IsPlaying(Shell* shell, const std::string& id) {
    return EvalJs(shell->web_contents(),
                  JsReplace("!document.getElementById($1).paused;", id))
        .ExtractBool();
  }

  WebAppSystemMediaControlsManager* GetWebAppSystemMediaControlsManager() {
    MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
        BrowserMainLoop::GetInstance()->media_keys_listener_manager();

    return media_keys_listener_manager_impl
        ->web_app_system_media_controls_manager_.get();
  }

  system_media_controls::SystemMediaControls* GetBrowserSystemMediaControls() {
    MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
        BrowserMainLoop::GetInstance()->media_keys_listener_manager();
    return media_keys_listener_manager_impl->browser_system_media_controls_
        .get();
  }

  system_media_controls::SystemMediaControls* GetSystemMediaControlsForWebApp(
      base::UnguessableToken request_id) {
    WebAppSystemMediaControls* web_app_system_media_controls =
        GetWebAppSystemMediaControlsManager()->GetControlsForRequestId(
            request_id);
    EXPECT_NE(web_app_system_media_controls, nullptr);

    system_media_controls::SystemMediaControls* system_media_controls =
        web_app_system_media_controls->GetSystemMediaControls();
    EXPECT_NE(system_media_controls, nullptr);

    return system_media_controls;
  }

  // This method asks the WebAppSystemMediaControlsManager to just assume
  // requests that come in come from a web app.
  void SetAlwaysAssumeWebAppForTesting() {
    GetWebAppSystemMediaControlsManager()->always_assume_web_app_for_testing_ =
        true;
  }

  void SetAlwaysIgnoreMediaSessionForTesting(WebContents* web_contents) {
    MediaSessionImpl* media_session = MediaSessionImpl::Get(web_contents);
    media_session->always_ignore_for_active_session_for_testing_ = true;
  }

  // This mechanism allows us to wait for a web app to be added to the
  // WebAppSystemMediaControls bookkeeping.
  void OnWebAppAdded(base::UnguessableToken request_id) override {
    last_web_app_request_id_ = request_id;
    web_app_added_run_loop_->Quit();
  }

  base::UnguessableToken WaitForWebAppAdded() {
    web_app_added_run_loop_->Run();
    EXPECT_FALSE(last_web_app_request_id_.is_empty());
    base::UnguessableToken cached_request_id = last_web_app_request_id_;
    last_web_app_request_id_ = base::UnguessableToken::Null();
    // Reset the runloop for the next use.
    web_app_added_run_loop_.emplace();
    return cached_request_id;
  }

  // This mechanism allows us to wait for MediaKeysListenerImpl to be ready
  // to listen to keys.
  void OnStartWatchingMediaKey(bool is_pwa) override {
    start_watching_media_key_run_loop_->Quit();
    last_watch_was_for_pwa_ = is_pwa;
  }

  // This function returns whether the last "watching key" event was for a PWA
  // or not.
  bool WaitForStartWatchingMediaKey() {
    start_watching_media_key_run_loop_->Run();
    EXPECT_TRUE(
        last_watch_was_for_pwa_);  // Check the value got set, optional resolves
                                   // to true if the value got set.
    bool cached_last_watch_was_for_pwa = last_watch_was_for_pwa_.value();
    last_watch_was_for_pwa_ = std::nullopt;
    // Reset the runloop for the next use.
    start_watching_media_key_run_loop_.emplace();
    return cached_last_watch_was_for_pwa;
  }

  // Waits for the visibility of the given WebApp's SystemMediaControls to match
  // `desired_visibility`. Returns true if the visibility has successfully
  // matched the expectation. Returns false if we time out before the state
  // changes.
  bool WaitForVisibility(const base::UnguessableToken& request_id,
                         bool desired_visibility) {
    // If the controls are already in the desired visibility state, early
    // return success.
    if (GetSystemMediaControlsForWebApp(request_id)
            ->GetVisibilityForTesting() == desired_visibility) {
      return true;
    }

    // Otherwise, wait for the visibility state to change.
    base::RunLoop wait_for_desired_visibility;
    auto visibility_changed_callback =
        base::BindLambdaForTesting([&](bool is_visible) {
          if (is_visible == desired_visibility) {
            wait_for_desired_visibility.Quit();
          }
        });

    system_media_controls::SystemMediaControls::
        SetVisibilityChangedCallbackForTesting(&visibility_changed_callback);
    wait_for_desired_visibility.Run();

    // Return true if the state is now correct.
    return GetSystemMediaControlsForWebApp(request_id)
               ->GetVisibilityForTesting() == desired_visibility;
  }

 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitchASCII(
        switches::kAutoplayPolicy,
        switches::autoplay::kNoUserGestureRequiredPolicy);

    feature_list_.InitAndEnableFeature(features::kWebAppSystemMediaControls);
  }

 private:
  std::optional<base::RunLoop> web_app_added_run_loop_;
  base::UnguessableToken last_web_app_request_id_;

  std::optional<base::RunLoop> browser_added_run_loop_;

  std::optional<base::RunLoop> start_watching_media_key_run_loop_;
  std::optional<bool> last_watch_was_for_pwa_;

  std::unique_ptr<net::EmbeddedTestServer> https_server_;
  base::test::ScopedFeatureList feature_list_;
};

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest,
                       SimpleOneBrowserTest) {
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  EXPECT_TRUE(NavigateToURL(shell(), http_url));

  // Run javascript to play the video, and wait for it to begin playing.
  StartPlaybackAndWait(shell(), "long-video-loop");
  // Check video is playing.
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

  bool is_for_pwa = WaitForStartWatchingMediaKey();
  EXPECT_FALSE(is_for_pwa);

  // Hit pause via simulating SMTC pause.
  MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();

  // Check video is still playing.
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

  media_keys_listener_manager_impl->OnPause(GetBrowserSystemMediaControls());

  // Check video is paused.
  WaitForStop(shell());
}

// TODO: crbug.com/361543620 - Fix the test on Win11 arm64 debug platform.
#if BUILDFLAG(IS_WIN) && !defined(NDEBUG) && defined(ARCH_CPU_ARM64)
#define MAYBE_ThreeBrowserTest DISABLED_ThreeBrowserTest
#else
#define MAYBE_ThreeBrowserTest ThreeBrowserTest
#endif
IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest,
                       MAYBE_ThreeBrowserTest) {
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));

  Shell* browser2 = CreateBrowser();
  Shell* browser3 = CreateBrowser();

  EXPECT_TRUE(NavigateToURL(shell(), http_url));
  EXPECT_TRUE(NavigateToURL(browser2, http_url));
  EXPECT_TRUE(NavigateToURL(browser3, http_url));

  // Press play and wait for each one to start.
  StartPlaybackAndWait(shell(), "long-video-loop");
  StartPlaybackAndWait(browser2, "long-video-loop");
  StartPlaybackAndWait(browser3, "long-video-loop");

  EXPECT_TRUE(IsPlaying(browser3, "long-video-loop"));
  EXPECT_TRUE(IsPlaying(browser2, "long-video-loop"));
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

  // Now we have 3 things playing at the same time. Browser 3 should have
  // control and be shown in SMTC.

  // Wait until MediaKeysListenerManagerImpl starts listening for keys.
  bool is_for_pwa = WaitForStartWatchingMediaKey();
  EXPECT_FALSE(is_for_pwa);

  // Hit pause via simulating SMTC pause.
  MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();
  media_keys_listener_manager_impl->OnPause(GetBrowserSystemMediaControls());

  // Check audio is paused for browser3.
  WaitForStop(browser3);

  // The other stuff should be continuing to loop.
  EXPECT_TRUE(IsPlaying(browser2, "long-video-loop"));
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest,
                       BrowserAndWebAppTest) {
  // Navigate two shells to the page.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  EXPECT_TRUE(NavigateToURL(shell(), http_url));

  Shell* web_app = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(web_app, http_url));

  // Start two playbacks, but set the testing flags so that the second window
  // will register as a web app to WebAppSystemMediaControlsManager.
  {
    StartPlaybackAndWait(shell(), "long-video-loop");
    EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

    // We need to be careful here that this first play is completely done before
    // we set the flag to pretend subsequent plays are from apps.
    bool is_for_pwa = WaitForStartWatchingMediaKey();
    EXPECT_FALSE(is_for_pwa);

    EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));
  }

  SetAlwaysAssumeWebAppForTesting();
  SetAlwaysIgnoreMediaSessionForTesting(web_app->web_contents());

  // Start the playback in the web app, wait for state to be ready, then hit
  // pause to the web app.

  StartPlaybackAndWait(web_app, "long-video-loop");
  base::UnguessableToken request_id = WaitForWebAppAdded();

  EXPECT_TRUE(IsPlaying(web_app, "long-video-loop"));

  EXPECT_FALSE(request_id.is_empty());

  // Wait for MediaKeysListenerManagerImpl to also start watching.
  bool is_for_pwa = WaitForStartWatchingMediaKey();
  EXPECT_TRUE(is_for_pwa);

  // Now retrieve the SMC and make a call to pause the video.
  system_media_controls::SystemMediaControls* system_media_controls =
      GetSystemMediaControlsForWebApp(request_id);

  MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();
  media_keys_listener_manager_impl->OnPause(system_media_controls);

  // The "web app" should be paused.
  WaitForStop(web_app);

  // The browser is still playing.
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

  // Now start the webapp again.
  media_keys_listener_manager_impl->OnPlay(system_media_controls);
  WaitForStart(web_app);

  // The browser is still playing.
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest, ThreeWebAppTest) {
  // Navigate two shells to the page.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  // We're mostly going to ignore this shell() based browser.

  Shell* web_app1 = CreateBrowser();
  Shell* web_app2 = CreateBrowser();
  Shell* web_app3 = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(web_app1, http_url));
  EXPECT_TRUE(NavigateToURL(web_app2, http_url));
  EXPECT_TRUE(NavigateToURL(web_app3, http_url));

  // Start all the playbacks.
  SetAlwaysAssumeWebAppForTesting();
  SetAlwaysIgnoreMediaSessionForTesting(web_app1->web_contents());
  SetAlwaysIgnoreMediaSessionForTesting(web_app2->web_contents());
  SetAlwaysIgnoreMediaSessionForTesting(web_app3->web_contents());

  base::UnguessableToken web_app1_request_id;
  base::UnguessableToken web_app2_request_id;
  base::UnguessableToken web_app3_request_id;

  {
    StartPlaybackAndWait(web_app1, "long-video-loop");
    web_app1_request_id = WaitForWebAppAdded();

    // Also wait until MediaKeysListenerManagerImpl starts listening for keys.
    bool is_for_pwa = WaitForStartWatchingMediaKey();
    EXPECT_TRUE(is_for_pwa);
  }

  {
    StartPlaybackAndWait(web_app2, "long-video-loop");
    web_app2_request_id = WaitForWebAppAdded();

    // Also wait until MediaKeysListenerManagerImpl starts listening for keys.
    bool is_for_pwa = WaitForStartWatchingMediaKey();
    EXPECT_TRUE(is_for_pwa);
  }

  {
    StartPlaybackAndWait(web_app3, "long-video-loop");
    web_app3_request_id = WaitForWebAppAdded();

    // Also wait until MediaKeysListenerManagerImpl starts listening for keys.
    bool is_for_pwa = WaitForStartWatchingMediaKey();
    EXPECT_TRUE(is_for_pwa);
  }

  // All request ids should be valid.
  EXPECT_NE(web_app1_request_id, base::UnguessableToken::Null());
  EXPECT_NE(web_app2_request_id, base::UnguessableToken::Null());
  EXPECT_NE(web_app3_request_id, base::UnguessableToken::Null());

  system_media_controls::SystemMediaControls* web_app1_system_media_controls =
      GetSystemMediaControlsForWebApp(web_app1_request_id);
  system_media_controls::SystemMediaControls* web_app2_system_media_controls =
      GetSystemMediaControlsForWebApp(web_app2_request_id);
  system_media_controls::SystemMediaControls* web_app3_system_media_controls =
      GetSystemMediaControlsForWebApp(web_app3_request_id);

  MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();

  media_keys_listener_manager_impl->OnPause(web_app2_system_media_controls);
  WaitForStop(web_app2);

  // The other stuff should be continuing to loop.
  EXPECT_TRUE(IsPlaying(web_app1, "long-video-loop"));
  EXPECT_TRUE(IsPlaying(web_app3, "long-video-loop"));

  // Pause 3, only 1 remains.
  media_keys_listener_manager_impl->OnPause(web_app3_system_media_controls);
  WaitForStop(web_app3);

  EXPECT_TRUE(IsPlaying(web_app1, "long-video-loop"));

  // Pause 1, only 1 remains.
  media_keys_listener_manager_impl->OnPause(web_app1_system_media_controls);
  WaitForStop(web_app1);
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest, TelemetryTest) {
  base::HistogramTester histogram_tester;
  Shell* web_app1 = CreateBrowser();
  Shell* web_app2 = CreateBrowser();

  // Navigate two shells to the page.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  EXPECT_TRUE(NavigateToURL(web_app1, http_url));
  EXPECT_TRUE(NavigateToURL(web_app2, http_url));

  // Start playback.
  SetAlwaysAssumeWebAppForTesting();
  SetAlwaysIgnoreMediaSessionForTesting(web_app1->web_contents());
  SetAlwaysIgnoreMediaSessionForTesting(web_app2->web_contents());

  // Load a video from web_app1.
  StartPlaybackAndWait(web_app1, "long-video-loop");
  base::UnguessableToken web_app1_request_id = WaitForWebAppAdded();
  EXPECT_TRUE(web_app1_request_id);
  EXPECT_TRUE(WaitForStartWatchingMediaKey());
  system_media_controls::SystemMediaControls* web_app1_system_media_controls =
      GetSystemMediaControlsForWebApp(web_app1_request_id);

  // Load a video from web_app2.
  StartPlaybackAndWait(web_app2, "long-video-loop");
  base::UnguessableToken web_app2_request_id = WaitForWebAppAdded();
  EXPECT_TRUE(WaitForStartWatchingMediaKey());
  system_media_controls::SystemMediaControls* web_app2_system_media_controls =
      GetSystemMediaControlsForWebApp(web_app2_request_id);

  MediaKeysListenerManagerImpl* media_keys_listener_manager =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();

  // Simulate a bunch of actions from SystemMediaTransportControls center.
  {
    media_keys_listener_manager->OnPause(web_app1_system_media_controls);
    WaitForStop(web_app1);
    media_keys_listener_manager->OnPlay(web_app1_system_media_controls);
    WaitForStart(web_app1);
    media_keys_listener_manager->OnPause(web_app2_system_media_controls);
    WaitForStop(web_app2);
    media_keys_listener_manager->OnPlay(web_app2_system_media_controls);
    WaitForStart(web_app2);

    FetchHistogramsFromChildProcesses();

    // 4 starts hence 4 PwaPlayingMedia events.
    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaPlayingMedia, 4);

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcPlay, 2);

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcPause, 2);
  }

  // Now just simulate some of the other ones. We can't do next/prev because
  // those are disabled when there are no next or previous tracks. so just skip
  // those.
  {
    media_keys_listener_manager->OnPlayPause(web_app1_system_media_controls);
    media_keys_listener_manager->OnStop(web_app1_system_media_controls);
    media_keys_listener_manager->OnSeek(web_app1_system_media_controls,
                                        base::Seconds(2));
    media_keys_listener_manager->OnSeekTo(web_app1_system_media_controls,
                                          base::Seconds(2));

    FetchHistogramsFromChildProcesses();

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcPlayPause, 1);

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcStop, 1);

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcSeek, 1);

    histogram_tester.ExpectBucketCount(
        "WebApp.Media.SystemMediaControls",
        WebAppSystemMediaControlsEvent::kPwaSmcSeekTo, 1);
  }
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest, TwoBrowserTest) {
  // Navigate two shells to the page.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  EXPECT_TRUE(NavigateToURL(shell(), http_url));

  Shell* browser2 = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(browser2, http_url));

  EXPECT_TRUE(NavigateToURL(shell(), http_url));
  EXPECT_TRUE(NavigateToURL(browser2, http_url));

  // Start two playbacks, both from the browser.
  {
    StartPlaybackAndWait(shell(), "long-video-loop");
    EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

    // Wait for the playing audio to register in the media keys listener
    // manager.
    WaitForStartWatchingMediaKey();

    StartPlaybackAndWait(browser2, "long-video-loop");
    EXPECT_TRUE(IsPlaying(browser2, "long-video-loop"));

    WaitForStartWatchingMediaKey();
  }

  // Now retrieve the SMC used for the browser and make a call to pause the
  // video.
  system_media_controls::SystemMediaControls* system_media_controls =
      GetBrowserSystemMediaControls();

  MediaKeysListenerManagerImpl* media_keys_listener_manager_impl =
      BrowserMainLoop::GetInstance()->media_keys_listener_manager();

  media_keys_listener_manager_impl->OnPause(system_media_controls);

  // The browser2 should be paused.
  WaitForStop(browser2);

  // The shell is still playing.
  EXPECT_TRUE(IsPlaying(shell(), "long-video-loop"));

  // Close browser2, this will cause the controlled browser to fall back to
  // shell.
  browser2->Close();

  // Wait a little for the fallback to occur.
  WaitForStartWatchingMediaKey();

  // Now hit pause again, it should pause shell().
  media_keys_listener_manager_impl->OnPause(system_media_controls);
  WaitForStop(shell());

  // The browser is still playing.
  EXPECT_FALSE(IsPlaying(shell(), "long-video-loop"));
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest,
                       SMTCHiddenOnNavigationAway) {
  // Set up a media session in 1 PWA.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  Shell* web_app = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(web_app, http_url));
  SetAlwaysAssumeWebAppForTesting();
  SetAlwaysIgnoreMediaSessionForTesting(web_app->web_contents());

  // Start the media session and wait for the controls to become visible.
  StartPlaybackAndWait(web_app, "long-video-loop");
  base::UnguessableToken request_id = WaitForWebAppAdded();
  EXPECT_TRUE(WaitForVisibility(request_id, /*desired_visibility=*/true));

  // Check the pwa is still playing, and navigate away to a different url.
  EXPECT_TRUE(IsPlaying(web_app, "long-video-loop"));
  GURL http_url2(https_server()->GetURL("/media/session/title1.html"));
  EXPECT_TRUE(NavigateToURL(web_app, http_url2));

  // The controls should hide now.
  EXPECT_TRUE(WaitForVisibility(request_id, /*desired_visibility=*/false));
}

IN_PROC_BROWSER_TEST_F(WebAppSystemMediaControlsBrowserTest,
                       SMTCHiddenOnAudioEnd) {
  // Set up a media session in 1 PWA.
  GURL http_url(https_server()->GetURL("/media/session/media-session.html"));
  Shell* web_app = CreateBrowser();
  EXPECT_TRUE(NavigateToURL(web_app, http_url));
  SetAlwaysAssumeWebAppForTesting();
  SetAlwaysIgnoreMediaSessionForTesting(web_app->web_contents());

  // Start the media session and wait for the controls to become visible.
  StartPlaybackAndWait(web_app, "short-video");
  base::UnguessableToken request_id = WaitForWebAppAdded();
  EXPECT_TRUE(WaitForVisibility(request_id, /*desired_visibility=*/true));

  // Wait for the audio track to end on its own.
  WaitForStop(web_app);
  EXPECT_FALSE(IsPlaying(web_app, "short-video"));

  // The controls should hide now.
  EXPECT_TRUE(WaitForVisibility(request_id, /*desired_visibility=*/false));
}

}  // namespace content