#include <memory>
#include <string>
#include <tuple>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
#include "chrome/browser/supervised_user/supervised_user_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/supervised_user/supervision_mixin.h"
#include "components/supervised_user/core/browser/permission_request_creator_mock.h"
#include "components/supervised_user/core/browser/supervised_user_interstitial.h"
#include "components/supervised_user/core/browser/supervised_user_service.h"
#include "components/supervised_user/core/browser/supervised_user_settings_service.h"
#include "components/supervised_user/core/browser/supervised_user_url_filter.h"
#include "components/supervised_user/core/browser/supervised_user_utils.h"
#include "components/supervised_user/core/common/features.h"
#include "components/supervised_user/core/common/features_testutils.h"
#include "components/supervised_user/core/common/supervised_user_constants.h"
#include "components/supervised_user/test_support/kids_management_api_server_mock.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/shell.h"
#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"
#include "ui/events/event_constants.h"
#include "ui/events/test/event_generator.h"
#endif
namespace {
NavigationController;
WebContents;
static const char* kExampleHost = …;
static const char* kExampleHost2 = …;
static const char* kStrippedExampleHost = …;
static const char* kFamiliesHost = …;
static const char* kIframeHost1 = …;
static const char* kIframeHost2 = …;
#if BUILDFLAG(IS_CHROMEOS_ASH)
constexpr char kLocalUrlAccessCommand[] = "requestUrlAccessLocal";
#endif
constexpr char kRemoteUrlAccessCommand[] = …;
class ThrottleTestParam { … };
class RenderFrameTracker : public content::WebContentsObserver { … };
void RenderFrameTracker::RenderFrameHostChanged(
content::RenderFrameHost* old_host,
content::RenderFrameHost* new_host) { … }
void RenderFrameTracker::FrameDeleted(int frame_tree_node_id) { … }
class InnerWebContentsAttachedWaiter : public content::WebContentsObserver { … };
void InnerWebContentsAttachedWaiter::InnerWebContentsAttached(
content::WebContents* inner_web_contents,
content::RenderFrameHost* render_frame_host,
bool is_full_page) { … }
void InnerWebContentsAttachedWaiter::WaitForInnerWebContentsAttached() { … }
class NavigationFinishedWaiter : public content::WebContentsObserver { … };
NavigationFinishedWaiter::NavigationFinishedWaiter(
content::WebContents* web_contents,
int frame_id,
const GURL& url)
: … { … }
void NavigationFinishedWaiter::Wait() { … }
void NavigationFinishedWaiter::DidFinishNavigation(
content::NavigationHandle* navigation_handle) { … }
class SupervisedUserNavigationThrottleTestBase
: public MixinBasedInProcessBrowserTest { … };
bool SupervisedUserNavigationThrottleTestBase::
IsInterstitialBeingShownInMainFrame(Browser* browser) { … }
void SupervisedUserNavigationThrottleTestBase::SetUp() { … }
void SupervisedUserNavigationThrottleTestBase::SetUpOnMainThread() { … }
class SupervisedUserNavigationThrottleTest
: public SupervisedUserNavigationThrottleTestBase,
::testing::WithParamInterface<ThrottleTestParam::FeatureStatus> { … };
class SupervisedUserNavigationThrottleWithPrerenderingTest
: public SupervisedUserNavigationThrottleTestBase,
public testing::WithParamInterface<
std::tuple<std::string, ThrottleTestParam::FeatureStatus>> { … };
INSTANTIATE_TEST_SUITE_P(…);
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_DisallowPrerendering …
#else
#define MAYBE_DisallowPrerendering …
#endif
IN_PROC_BROWSER_TEST_P(SupervisedUserNavigationThrottleWithPrerenderingTest,
MAYBE_DisallowPrerendering) { … }
IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
NoNavigationObserverBlock) { … }
IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
BlockMainFrameWithInterstitial) { … }
IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
DontBlockSubFrame) { … }
IN_PROC_BROWSER_TEST_F(SupervisedUserNavigationThrottleTest,
AllowFamiliesDotGoogleDotComAccess) { … }
class SupervisedUserIframeFilterTest
: public SupervisedUserNavigationThrottleTestBase,
public testing::WithParamInterface<
std::tuple<supervised_user::testing::LocalWebApprovalsTestCase,
ThrottleTestParam::FeatureStatus>> { … };
void SupervisedUserIframeFilterTest::SetUpOnMainThread() { … }
void SupervisedUserIframeFilterTest::TearDownOnMainThread() { … }
std::vector<int> SupervisedUserIframeFilterTest::GetBlockedFrames() { … }
const GURL& SupervisedUserIframeFilterTest::GetBlockedFrameURL(int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsInterstitialBeingShownInFrame(
int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsBlockReasonBeingShown(int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsDetailsLinkBeingShown(int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsRemoteApprovalsButtonBeingShown(
int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsLocalApprovalsButtonBeingShown(
int frame_id) { … }
void SupervisedUserIframeFilterTest::CheckPreferredApprovalButton(
int frame_id) { … }
bool SupervisedUserIframeFilterTest::IsLocalApprovalsInsteadButtonBeingShown(
int frame_id) { … }
void SupervisedUserIframeFilterTest::SendCommandToFrame(
const std::string& command_name,
int frame_id) { … }
void SupervisedUserIframeFilterTest::WaitForNavigationFinished(
int frame_id,
const GURL& url) { … }
bool SupervisedUserIframeFilterTest::RunCommandAndGetBooleanFromFrame(
int frame_id,
const std::string& command) { … }
bool SupervisedUserIframeFilterTest::IsLocalWebApprovalsEnabled() const { … }
INSTANTIATE_TEST_SUITE_P(…);
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest, BlockSubFrame) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest, BlockMultipleSubFrames) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest, TestBackButton) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
TestBackButtonMainFrame) { … }
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_BlockedMainFrameFromClassifyUrlForUnstripedHostIsStrippedInRemoteApproval …
#else
#define MAYBE_BlockedMainFrameFromClassifyUrlForUnstripedHostIsStrippedInRemoteApproval …
#endif
IN_PROC_BROWSER_TEST_P(
SupervisedUserIframeFilterTest,
MAYBE_BlockedMainFrameFromClassifyUrlForUnstripedHostIsStrippedInRemoteApproval) { … }
IN_PROC_BROWSER_TEST_P(
SupervisedUserIframeFilterTest,
BlockedMainFrameFromBlockListIsStrippedInRemoteApproval) { … }
IN_PROC_BROWSER_TEST_P(
SupervisedUserIframeFilterTest,
BlockedMainFrameFromBlockListForUnstripedHostSkipsStrippingInRemoteApproval) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
AllowlistedMainFrameDenylistedIframe) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
RememberAlreadyRequestedHosts) { … }
IN_PROC_BROWSER_TEST_P(SupervisedUserIframeFilterTest,
IFramesWithSameDomainAsMainFrameAllowed) { … }
#if BUILDFLAG(IS_CHROMEOS_ASH)
class SupervisedUserNarrowWidthIframeFilterTest
: public SupervisedUserIframeFilterTest {
protected:
SupervisedUserNarrowWidthIframeFilterTest() = default;
~SupervisedUserNarrowWidthIframeFilterTest() override = default;
void SetUp() override;
};
void SupervisedUserNarrowWidthIframeFilterTest::SetUp() {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
::switches::kHostWindowBounds, "0+0-400x800");
SupervisedUserIframeFilterTest::SetUp();
}
INSTANTIATE_TEST_SUITE_P(
LocalWebApprovalsEnabledNarrowWidth,
SupervisedUserNarrowWidthIframeFilterTest,
testing::Combine(
supervised_user::testing::LocalWebApprovalsTestCase::OnlySupported(),
ThrottleTestParam::Values()),
[](const testing::TestParamInfo<
std::tuple<supervised_user::testing::LocalWebApprovalsTestCase,
ThrottleTestParam::FeatureStatus>>& info) {
return base::StrCat(
{"WithLocalWebApprovalsTestCase",
static_cast<std::string>(std::get<0>(info.param)), "WithThrottle",
ThrottleTestParam::ToString(std::get<1>(info.param))});
});
IN_PROC_BROWSER_TEST_P(SupervisedUserNarrowWidthIframeFilterTest,
NarrowWidthWindow) {
BlockHost(kExampleHost);
GURL blocked_url = embedded_test_server()->GetURL(
kExampleHost, "/supervised_user/with_iframes.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blocked_url));
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
auto blocked_frames = GetBlockedFrames();
EXPECT_EQ(blocked_frames.size(), 1u);
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frames[0]));
EXPECT_EQ(IsLocalWebApprovalsEnabled(),
IsLocalApprovalsButtonBeingShown(blocked_frames[0]));
EXPECT_TRUE(IsDetailsLinkBeingShown(blocked_frames[0]));
permission_creator()->SetPermissionResult(true);
permission_creator()->DelayHandlingForNextRequests();
SendCommandToFrame(kRemoteUrlAccessCommand, blocked_frames[0]);
GURL allowed_url = embedded_test_server()->GetURL(
kExampleHost2, "/supervised_user/with_iframes.html");
kids_management_api_mock().AllowSubsequentClassifyUrl();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
EXPECT_FALSE(IsInterstitialBeingShownInMainFrame(browser()));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blocked_url));
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
EXPECT_FALSE(IsRemoteApprovalsButtonBeingShown(blocked_frames[0]));
EXPECT_EQ(IsLocalWebApprovalsEnabled(),
IsLocalApprovalsInsteadButtonBeingShown(blocked_frames[0]));
EXPECT_FALSE(IsDetailsLinkBeingShown(blocked_frames[0]));
content::WebContents* active_contents =
browser()->tab_strip_model()->GetActiveWebContents();
SupervisedUserNavigationObserver* navigation_observer =
SupervisedUserNavigationObserver::FromWebContents(active_contents);
ASSERT_NE(navigation_observer, nullptr);
EXPECT_TRUE(base::Contains(navigation_observer->requested_hosts_for_test(),
kExampleHost));
NavigationFinishedWaiter waiter(
active_contents,
active_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
blocked_url);
permission_creator()->HandleDelayedRequests();
waiter.Wait();
EXPECT_FALSE(base::Contains(navigation_observer->requested_hosts_for_test(),
kExampleHost));
EXPECT_FALSE(IsInterstitialBeingShownInMainFrame(browser()));
}
using ChromeOSLocalWebApprovalsTest = SupervisedUserIframeFilterTest;
INSTANTIATE_TEST_SUITE_P(
,
ChromeOSLocalWebApprovalsTest,
testing::Combine(
supervised_user::testing::LocalWebApprovalsTestCase::OnlySupported(),
ThrottleTestParam::Values()),
[](const testing::TestParamInfo<
std::tuple<supervised_user::testing::LocalWebApprovalsTestCase,
ThrottleTestParam::FeatureStatus>>& info) {
return base::StrCat(
{"WithLocalWebApprovalsTestCase",
static_cast<std::string>(std::get<0>(info.param)), "WithThrottle",
ThrottleTestParam::ToString(std::get<1>(info.param))});
});
IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
StartLocalWebApprovalsFromMainFrame) {
base::HistogramTester histogram_tester;
BlockHost(kExampleHost);
GURL blocked_url = embedded_test_server()->GetURL(
kExampleHost, "/supervised_user/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blocked_url));
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
const std::vector<int> blocked_frames = GetBlockedFrames();
ASSERT_EQ(blocked_frames.size(), 1u);
const int blocked_frame = blocked_frames[0];
EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
CheckPreferredApprovalButton(blocked_frame);
SendCommandToFrame(kLocalUrlAccessCommand, blocked_frame);
EXPECT_TRUE(ash::ParentAccessDialog::GetInstance());
ui::test::EventGenerator generator(ash::Shell::Get()->GetPrimaryRootWindow());
generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
EXPECT_FALSE(ash::ParentAccessDialog::GetInstance());
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
CheckPreferredApprovalButton(blocked_frame);
histogram_tester.ExpectUniqueSample(
supervised_user::SupervisedUserInterstitial::
kInterstitialCommandHistogramName,
supervised_user::SupervisedUserInterstitial::Commands::
LOCAL_ACCESS_REQUEST,
1);
histogram_tester.ExpectUniqueSample(
supervised_user::SupervisedUserInterstitial::
kInterstitialPermissionSourceHistogramName,
supervised_user::SupervisedUserInterstitial::RequestPermissionSource::
MAIN_FRAME,
1);
}
IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
StartLocalWebApprovalsFromIframe) {
base::HistogramTester histogram_tester;
BlockHost(kIframeHost1);
const GURL allowed_url_with_iframes = embedded_test_server()->GetURL(
kExampleHost, "/supervised_user/with_iframes.html");
kids_management_api_mock().AllowSubsequentClassifyUrl();
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), allowed_url_with_iframes));
EXPECT_FALSE(IsInterstitialBeingShownInMainFrame(browser()));
const std::vector<int> blocked_frames = GetBlockedFrames();
ASSERT_EQ(blocked_frames.size(), 1u);
const int blocked_frame = blocked_frames[0];
EXPECT_TRUE(IsInterstitialBeingShownInFrame(blocked_frame));
EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
CheckPreferredApprovalButton(blocked_frame);
SendCommandToFrame(kLocalUrlAccessCommand, blocked_frame);
EXPECT_TRUE(ash::ParentAccessDialog::GetInstance());
ui::test::EventGenerator generator(ash::Shell::Get()->GetPrimaryRootWindow());
generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
EXPECT_FALSE(ash::ParentAccessDialog::GetInstance());
EXPECT_FALSE(IsInterstitialBeingShownInMainFrame(browser()));
EXPECT_TRUE(IsInterstitialBeingShownInFrame(blocked_frame));
EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
CheckPreferredApprovalButton(blocked_frame);
histogram_tester.ExpectUniqueSample(
supervised_user::SupervisedUserInterstitial::
kInterstitialCommandHistogramName,
supervised_user::SupervisedUserInterstitial::Commands::
LOCAL_ACCESS_REQUEST,
1);
histogram_tester.ExpectUniqueSample(
supervised_user::SupervisedUserInterstitial::
kInterstitialPermissionSourceHistogramName,
supervised_user::SupervisedUserInterstitial::RequestPermissionSource::
SUB_FRAME,
1);
}
IN_PROC_BROWSER_TEST_P(ChromeOSLocalWebApprovalsTest,
UpdateUIAfterRemoteRequestSent) {
BlockHost(kExampleHost);
GURL blocked_url = embedded_test_server()->GetURL(
kExampleHost, "/supervised_user/simple.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blocked_url));
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
const std::vector<int> blocked_frames = GetBlockedFrames();
ASSERT_EQ(blocked_frames.size(), 1u);
const int blocked_frame = blocked_frames[0];
EXPECT_TRUE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
SendCommandToFrame(kRemoteUrlAccessCommand, blocked_frame);
EXPECT_TRUE(IsInterstitialBeingShownInMainFrame(browser()));
EXPECT_FALSE(IsLocalApprovalsButtonBeingShown(blocked_frame));
EXPECT_FALSE(IsRemoteApprovalsButtonBeingShown(blocked_frame));
EXPECT_TRUE(IsLocalApprovalsInsteadButtonBeingShown(blocked_frame));
}
#endif
class SupervisedUserNavigationThrottleOnlyEnabledForSupervisedUsers
: public SupervisedUserNavigationThrottleTestBase,
public testing::WithParamInterface<
std::tuple<supervised_user::SupervisionMixin::SignInMode,
ThrottleTestParam::FeatureStatus>> { … };
INSTANTIATE_TEST_SUITE_P(…);
IN_PROC_BROWSER_TEST_P(
SupervisedUserNavigationThrottleOnlyEnabledForSupervisedUsers,
CheckAgainstBlocklist) { … }
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_CheckAgainstClassifyUrlRPC …
#else
#define MAYBE_CheckAgainstClassifyUrlRPC …
#endif
IN_PROC_BROWSER_TEST_P(
SupervisedUserNavigationThrottleOnlyEnabledForSupervisedUsers,
MAYBE_CheckAgainstClassifyUrlRPC) { … }
#if BUILDFLAG(IS_CHROMEOS)
#define MAYBE_CheckSameDocumentNavigationAgainstClassifyUrlRPC …
#else
#define MAYBE_CheckSameDocumentNavigationAgainstClassifyUrlRPC …
#endif
IN_PROC_BROWSER_TEST_P(
SupervisedUserNavigationThrottleOnlyEnabledForSupervisedUsers,
MAYBE_CheckSameDocumentNavigationAgainstClassifyUrlRPC) { … }
class SupervisedUserNavigationThrottleFencedFramesTest
: public SupervisedUserNavigationThrottleTestBase,
public testing::WithParamInterface<ThrottleTestParam::FeatureStatus> { … };
INSTANTIATE_TEST_SUITE_P(…);
IN_PROC_BROWSER_TEST_P(SupervisedUserNavigationThrottleFencedFramesTest,
BlockFencedFrame) { … }
}