chromium/chrome/browser/lacros/fullscreen_controller_client_lacros_unittest.cc

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

#include "chrome/browser/lacros/fullscreen_controller_client_lacros.h"

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/test/test_future.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/apps/chrome_app_delegate.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/test_browser_window.h"
#include "chromeos/crosapi/mojom/fullscreen_controller.mojom.h"
#include "chromeos/ui/wm/fullscreen/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/app_window/app_window.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/test_app_window_contents.h"
#include "extensions/common/extension_builder.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/window.h"
#include "ui/views/controls/webview/webview.h"
#include "url/gurl.h"

namespace chromeos {

using crosapi::mojom::FullscreenController;
using crosapi::mojom::FullscreenControllerClient;

namespace {

constexpr char kActiveUrl[] = "https://wwww.test.com";

constexpr char kNonMatchingPattern[] = "google.com";
constexpr char kMatchingPattern[] = "test.com";
constexpr char kWildcardPattern[] = "*";

enum class TestWebContentsChoice {
  kAppWindow,
  kBrowserWindow,
};

std::string ParamToString(
    const testing::TestParamInfo<TestWebContentsChoice>& info) {
  switch (info.param) {
    case TestWebContentsChoice::kAppWindow:
      return "AppWindow";
    case TestWebContentsChoice::kBrowserWindow:
      return "BrowserWindow";
  }
}

class MockRemote : public FullscreenController {
 public:
  // FullscreenController:
  void AddClient(
      mojo::PendingRemote<FullscreenControllerClient> client) override {
    remote_.Bind(std::move(client));
  }

  void ShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback) {
    remote_->ShouldExitFullscreenBeforeLock(
        base::BindOnce(&MockRemote::OnShouldExitFullscreenBeforeLock,
                       base::Unretained(this), std::move(callback)));
  }

  void OnShouldExitFullscreenBeforeLock(base::OnceCallback<void(bool)> callback,
                                        bool should_exit_fullscreen) {
    std::move(callback).Run(should_exit_fullscreen);
  }

 private:
  mojo::Receiver<FullscreenController> receiver_{this};
  mojo::Remote<FullscreenControllerClient> remote_;
};

class TestNativeAppWindow : public ChromeNativeAppWindowViewsAura {
 public:
  TestNativeAppWindow() {
    set_web_view_for_testing(
        AddChildView(std::make_unique<views::WebView>(nullptr)));
  }
  ~TestNativeAppWindow() override {}

  bool IsActive() const override { return true; }
};

}  // namespace

class FullscreenControllerClientLacrosTest : public BrowserWithTestWindowTest {
 public:
  void SetUp() override {
    BrowserWithTestWindowTest::SetUp();

    profile_ = ProfileManager::GetPrimaryUserProfile();

    // Set the active profile.
    PrefService* local_state = g_browser_process->local_state();
    local_state->SetString(::prefs::kProfileLastUsed,
                           profile_->GetBaseName().value());
  }

  void TearDown() override {
    profile_ = nullptr;
    BrowserWithTestWindowTest::TearDown();
  }

  void SetKeepFullscreenWithoutNotificationAllowList(
      const std::string& pattern) {
    base::Value::List list;
    list.Append(pattern);
    profile_->GetPrefs()->SetList(
        prefs::kKeepFullscreenWithoutNotificationUrlAllowList, std::move(list));
  }

  void RunTest(bool expect_should_exit_fullscreen) {
    base::test::TestFuture<bool> future;

    FullscreenControllerClientLacros client;
    mojo::Receiver<FullscreenControllerClient> receiver{&client};
    mock_.AddClient(receiver.BindNewPipeAndPassRemote());
    mock_.ShouldExitFullscreenBeforeLock(future.GetCallback());

    bool should_exit_fullscreen = future.Take();
    ASSERT_EQ(should_exit_fullscreen, expect_should_exit_fullscreen);
  }

 protected:
  raw_ptr<Profile> profile_ = nullptr;
  testing::StrictMock<MockRemote> mock_;
};

// Test that ShouldExitFullscreenBeforeLock() returns true if the WebContent is
// not found and the allow list is unset.
TEST_F(FullscreenControllerClientLacrosTest,
       ExitFullscreenIfWebContentsUnavailableUnsetPref) {
  RunTest(/*expect_should_exit_fullscreen=*/true);
}

// Test that ShouldExitFullscreenBeforeLock() returns true if the WebContent is
// not found and the allow list includes the wildcard character.
TEST_F(FullscreenControllerClientLacrosTest,
       ExitFullscreenIfWebContentsUnavailableWildcardPref) {
  SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern);

  RunTest(/*expect_should_exit_fullscreen=*/true);
}

class FullscreenControllerClientLacrosWebContentsTest
    : public FullscreenControllerClientLacrosTest,
      public testing::WithParamInterface<TestWebContentsChoice> {
 public:
  void SetUp() override {
    FullscreenControllerClientLacrosTest::SetUp();

    switch (GetParam()) {
      case TestWebContentsChoice::kAppWindow:
        AddAppWindow();
        break;
      case TestWebContentsChoice::kBrowserWindow:
        AddBrowserWindow();
        break;
    }
  }

  void TearDown() override {
    if (app_window_) {
      // OnNativeClose() will destroy the app window.
      app_window_.ExtractAsDangling()->OnNativeClose();
    }

    FullscreenControllerClientLacrosTest::TearDown();
  }

  void AddAppWindow() {
    // Create a new AppWindow
    scoped_refptr<const extensions::Extension> extension =
        extensions::ExtensionBuilder("test extension").Build();
    app_window_ = new extensions::AppWindow(
        profile_, std::make_unique<ChromeAppDelegate>(profile_, true),
        extension.get());

    // Set the active WebContents
    std::unique_ptr<content::WebContents> contents(content::WebContents::Create(
        content::WebContents::CreateParams(app_window_->browser_context())));
    content::NavigationSimulator::NavigateAndCommitFromBrowser(
        contents.get(), GURL(kActiveUrl));
    app_window_->SetAppWindowContentsForTesting(
        std::make_unique<extensions::TestAppWindowContents>(
            std::move(contents)));

    // Set the native app window
    app_window_->SetNativeAppWindowForTesting(
        std::make_unique<TestNativeAppWindow>());

    extensions::AppWindowRegistry::Get(profile_)->AddAppWindow(app_window_);
  }

  void AddBrowserWindow() {
    AddTab(browser(), GURL(kActiveUrl));
    static_cast<TestBrowserWindow*>(browser()->window())->set_is_active(true);
    ASSERT_TRUE(chrome::FindBrowserWithActiveWindow());
  }

 protected:
  raw_ptr<extensions::AppWindow> app_window_ = nullptr;
};

// Test that ShouldExitFullscreenBeforeLock() returns true if the allow list
// pref is unset.
TEST_P(FullscreenControllerClientLacrosWebContentsTest,
       ExitFullscreenIfUnsetPref) {
  RunTest(/*expect_should_exit_fullscreen=*/true);
}

// Test that ShouldExitFullscreenBeforeLock() returns true if the URL of the
// active window does not match any patterns from the allow list.
TEST_P(FullscreenControllerClientLacrosWebContentsTest,
       ExitFullscreenIfNonMatchingPref) {
  SetKeepFullscreenWithoutNotificationAllowList(kNonMatchingPattern);

  RunTest(/*expect_should_exit_fullscreen=*/true);
}

// Test that ShouldExitFullscreenBeforeLock() returns false if the URL of the
// active window matches a pattern from the allow list.
TEST_P(FullscreenControllerClientLacrosWebContentsTest,
       KeepFullscreenIfMatchingPref) {
  // Set up the URL exempt list with one matching and one non-matching pattern.
  base::Value::List list;
  list.Append(kNonMatchingPattern);
  list.Append(kMatchingPattern);
  profile_->GetPrefs()->SetList(
      prefs::kKeepFullscreenWithoutNotificationUrlAllowList, std::move(list));

  RunTest(/*expect_should_exit_fullscreen=*/false);
}

// Test that ShouldExitFullscreenBeforeLock() returns false if the allow list
// includes the wildcard character.
TEST_P(FullscreenControllerClientLacrosWebContentsTest,
       KeepFullscreenIfWildcardPref) {
  SetKeepFullscreenWithoutNotificationAllowList(kWildcardPattern);

  RunTest(/*expect_should_exit_fullscreen=*/false);
}

INSTANTIATE_TEST_SUITE_P(
    All,
    FullscreenControllerClientLacrosWebContentsTest,
    ::testing::Values(TestWebContentsChoice::kAppWindow,
                      TestWebContentsChoice::kBrowserWindow),
    &ParamToString);

}  // namespace chromeos