#include "components/site_engagement/content/site_engagement_helper.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/timer/timer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/site_engagement/content/engagement_type.h"
#include "components/site_engagement/content/site_engagement_metrics.h"
#include "components/site_engagement/content/site_engagement_observer.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace site_engagement {
class TestOneShotTimer : public base::OneShotTimer {
public:
TestOneShotTimer() = default;
~TestOneShotTimer() override = default;
void Start(const base::Location& posted_from,
base::TimeDelta delay,
base::OnceClosure user_task) override {
base::OneShotTimer::Start(posted_from, base::Seconds(0),
std::move(user_task));
if (started_)
restarted_ = true;
started_ = true;
}
bool restarted() { return restarted_; }
private:
bool started_ = false;
bool restarted_ = false;
};
class SiteEngagementHelperBrowserTest : public InProcessBrowserTest {
public:
SiteEngagementHelperBrowserTest()
: prerender_helper_(
base::BindRepeating(&SiteEngagementHelperBrowserTest::web_contents,
base::Unretained(this))) {}
~SiteEngagementHelperBrowserTest() override = default;
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(test_server_handle_ =
embedded_test_server()->StartAndReturnHandle());
}
void SetInputTrackerPauseTimer(SiteEngagementService::Helper* helper) {
input_tracker_timer_ = new TestOneShotTimer;
helper->input_tracker_.SetPauseTimerForTesting(
base::WrapUnique(input_tracker_timer_.get()));
}
void SetMediaTrackerPauseTimer(SiteEngagementService::Helper* helper) {
media_tracker_timer_ = new TestOneShotTimer;
helper->media_tracker_.SetPauseTimerForTesting(
base::WrapUnique(media_tracker_timer_.get()));
}
bool IsInputTrackerTimerRestarted(SiteEngagementService::Helper* helper) {
return input_tracker_timer_->restarted();
}
content::test::PrerenderTestHelper* prerender_helper() {
return &prerender_helper_;
}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
base::HistogramTester* histogram_tester() { return &histogram_tester_; }
private:
content::test::PrerenderTestHelper prerender_helper_;
net::test_server::EmbeddedTestServerHandle test_server_handle_;
base::HistogramTester histogram_tester_;
raw_ptr<TestOneShotTimer, AcrossTasksDanglingUntriaged> input_tracker_timer_;
raw_ptr<TestOneShotTimer, AcrossTasksDanglingUntriaged> media_tracker_timer_;
};
IN_PROC_BROWSER_TEST_F(SiteEngagementHelperBrowserTest,
SiteEngagementHelperInPrerendering) {
SiteEngagementService::Helper* helper =
SiteEngagementService::Helper::FromWebContents(web_contents());
SetInputTrackerPauseTimer(helper);
GURL url = embedded_test_server()->GetURL("/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
histogram_tester()->ExpectTotalCount(
SiteEngagementMetrics::kEngagementTypeHistogram, 2);
EXPECT_FALSE(IsInputTrackerTimerRestarted(helper));
auto prerender_url = embedded_test_server()->GetURL("/simple.html");
content::FrameTreeNodeId host_id =
prerender_helper()->AddPrerender(prerender_url);
content::test::PrerenderHostObserver host_observer(*web_contents(), host_id);
histogram_tester()->ExpectTotalCount(
SiteEngagementMetrics::kEngagementTypeHistogram, 2);
EXPECT_FALSE(IsInputTrackerTimerRestarted(helper));
prerender_helper()->NavigatePrimaryPage(*web_contents(), prerender_url);
EXPECT_TRUE(host_observer.was_activated());
EXPECT_TRUE(IsInputTrackerTimerRestarted(helper));
histogram_tester()->ExpectTotalCount(
SiteEngagementMetrics::kEngagementTypeHistogram, 2);
histogram_tester()->ExpectBucketCount(
SiteEngagementMetrics::kEngagementTypeHistogram,
EngagementType::kNavigation, 1);
}
class ObserverTester : public SiteEngagementObserver {
public:
explicit ObserverTester(SiteEngagementService* service)
: SiteEngagementObserver(service) {}
void OnEngagementEvent(content::WebContents* web_contents,
const GURL& url,
double score,
double old_score,
EngagementType type,
const std::optional<webapps::AppId>& app_id) override {
last_updated_type_ = type;
last_updated_url_ = url;
if (type == type_waiting_) {
if (quit_closure_)
std::move(quit_closure_).Run();
}
}
void WaitForEngagementEvent(EngagementType type) {
type_waiting_ = type;
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
EngagementType last_updated_type() { return last_updated_type_; }
const GURL& last_updated_url() { return last_updated_url_; }
private:
base::OnceClosure quit_closure_;
GURL last_updated_url_;
EngagementType last_updated_type_ = EngagementType::kLast;
EngagementType type_waiting_ = EngagementType::kLast;
};
IN_PROC_BROWSER_TEST_F(SiteEngagementHelperBrowserTest,
SiteEngagementHelperMediaTrackerInPrerendering) {
site_engagement::SiteEngagementService* service =
site_engagement::SiteEngagementService::Get(browser()->profile());
ObserverTester tester(service);
SiteEngagementService::Helper* helper =
SiteEngagementService::Helper::FromWebContents(web_contents());
SetMediaTrackerPauseTimer(helper);
GURL url = embedded_test_server()->GetURL("/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_EQ(tester.last_updated_type(), EngagementType::kNavigation);
EXPECT_EQ(tester.last_updated_url(), url);
GURL prerender_url =
embedded_test_server()->GetURL("/media/unified_autoplay.html");
content::FrameTreeNodeId host_id =
prerender_helper()->AddPrerender(prerender_url);
content::test::PrerenderHostObserver host_observer(*web_contents(), host_id);
content::RenderFrameHost* prerendered_frame_host =
prerender_helper()->GetPrerenderedMainFrameHost(host_id);
EXPECT_EQ(false, content::EvalJs(
prerendered_frame_host, "attemptPlay();",
content::EvalJsOptions::EXECUTE_SCRIPT_NO_USER_GESTURE));
EXPECT_EQ(tester.last_updated_type(), EngagementType::kNavigation);
EXPECT_EQ(tester.last_updated_url(), url);
prerender_helper()->NavigatePrimaryPage(prerender_url);
EXPECT_TRUE(host_observer.was_activated());
EXPECT_TRUE(
content::EvalJs(web_contents()->GetPrimaryMainFrame(), "attemptPlay();")
.ExtractBool());
tester.WaitForEngagementEvent(EngagementType::kMediaVisible);
EXPECT_EQ(tester.last_updated_type(), EngagementType::kMediaVisible);
EXPECT_EQ(tester.last_updated_url(), prerender_url);
}
}