// 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