#include "base/strings/strcat.h"
#include "base/test/bind.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_switches.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/test_navigation_observer.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/shell/common/shell_switches.h"
using content::URLLoaderInterceptor;
namespace {
constexpr char kBaseDataDir[] = "content/test/data/origin_trials/";
void NavigateViaRenderer(content::WebContents* web_contents, const GURL& url) {
EXPECT_TRUE(
content::ExecJs(web_contents->GetPrimaryMainFrame(),
base::StrCat({"location.href='", url.spec(), "';"})));
EXPECT_TRUE(content::ExecJs(web_contents->GetPrimaryMainFrame(), "true"));
EXPECT_TRUE(content::WaitForLoadStop(web_contents));
EXPECT_EQ(web_contents->GetLastCommittedURL(), url);
}
}
namespace content {
class OriginTrialsBrowserTest : public content::ContentBrowserTest {
public:
OriginTrialsBrowserTest() : ContentBrowserTest() {}
OriginTrialsBrowserTest(const OriginTrialsBrowserTest&) = delete;
OriginTrialsBrowserTest& operator=(const OriginTrialsBrowserTest&) = delete;
~OriginTrialsBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kExposeInternalsForTesting);
}
void SetUpOnMainThread() override {
ContentBrowserTest::SetUpOnMainThread();
url_loader_interceptor_ =
std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting(
[&](URLLoaderInterceptor::RequestParams* params) -> bool {
URLLoaderInterceptor::WriteResponse(
base::StrCat({kBaseDataDir, params->url_request.url.path()}),
params->client.get());
return true;
}));
}
void TearDownOnMainThread() override {
url_loader_interceptor_.reset();
ContentBrowserTest::TearDownOnMainThread();
}
RenderFrameHost* GetFrameByName(const std::string frame_name) {
return FrameMatchingPredicate(
shell()->web_contents()->GetPrimaryPage(),
base::BindRepeating(FrameMatchesName, frame_name));
}
RenderFrameHost* GetPrimaryMainFrame() {
return shell()->web_contents()->GetPrimaryMainFrame();
}
testing::AssertionResult HasTrialEnabled(RenderFrameHost* frame) {
return content::ExecJs(frame,
"internals.originTrialsTest().normalMethod();");
}
testing::AssertionResult HasNavigationTrialEnabled(RenderFrameHost* frame) {
return content::ExecJs(frame,
"internals.originTrialsTest().navigationMethod();");
}
private:
std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
};
IN_PROC_BROWSER_TEST_F(OriginTrialsBrowserTest, Basic) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("https://example.test/basic.html")));
EXPECT_TRUE(HasTrialEnabled(GetPrimaryMainFrame()));
}
IN_PROC_BROWSER_TEST_F(OriginTrialsBrowserTest,
NonNavigationTrialNotActivatedAcrossNavigations) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("https://example.test/basic.html")));
EXPECT_TRUE(HasTrialEnabled(GetPrimaryMainFrame()));
NavigateViaRenderer(shell()->web_contents(),
GURL("https://other.test/notrial.html"));
EXPECT_FALSE(HasTrialEnabled(GetPrimaryMainFrame()));
}
IN_PROC_BROWSER_TEST_F(OriginTrialsBrowserTest, Navigation) {
EXPECT_TRUE(
NavigateToURL(shell(), GURL("https://example.test/navigation.html")));
EXPECT_TRUE(HasNavigationTrialEnabled(GetPrimaryMainFrame()));
}
IN_PROC_BROWSER_TEST_F(OriginTrialsBrowserTest,
NavigationTrialActivatedAcrossNavigations) {
EXPECT_TRUE(
NavigateToURL(shell(), GURL("https://example.test/navigation.html")));
EXPECT_TRUE(HasNavigationTrialEnabled(GetPrimaryMainFrame()));
NavigateViaRenderer(shell()->web_contents(),
GURL("https://other.test/notrial.html"));
EXPECT_TRUE(HasNavigationTrialEnabled(GetPrimaryMainFrame()));
NavigateViaRenderer(shell()->web_contents(),
GURL("https://other.test/basic.html"));
EXPECT_FALSE(HasNavigationTrialEnabled(GetPrimaryMainFrame()));
}
const char kCallWorkerScript[] =
"(() => {"
" const worker = new Worker('/worker.js');"
" const waitResult = new Promise((resolve, reject) => {"
" worker.onmessage = function(e) {"
" (e.data?resolve:reject)(`return ${e.data} from worker`);"
" };"
" });"
" worker.postMessage('ping');"
" return waitResult;"
"})()";
class ForceEnabledOriginTrialsBrowserTest
: public OriginTrialsBrowserTest,
public testing::WithParamInterface<bool>,
public WebContentsObserver {
public:
ForceEnabledOriginTrialsBrowserTest() = default;
~ForceEnabledOriginTrialsBrowserTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
OriginTrialsBrowserTest::SetUpCommandLine(command_line);
if (disable_site_isolation_)
command_line->AppendSwitch(switches::kDisableSiteIsolation);
}
void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override {
if (navigation_handle->GetURL() == url_to_enable_trial_) {
navigation_handle->ForceEnableOriginTrials(
std::vector<std::string>({"Frobulate"}));
}
}
void set_url_to_enable_trial(const GURL& url) { url_to_enable_trial_ = url; }
protected:
bool disable_site_isolation_ = GetParam();
const GURL main_url_ =
GURL("https://example.test/force_enabled_main_frame.html");
private:
GURL url_to_enable_trial_;
};
IN_PROC_BROWSER_TEST_P(ForceEnabledOriginTrialsBrowserTest,
ForceEnabledOriginTrials_MainPage) {
set_url_to_enable_trial(main_url_);
Observe(shell()->web_contents());
EXPECT_TRUE(NavigateToURL(shell(), main_url_));
EXPECT_TRUE(HasTrialEnabled(GetPrimaryMainFrame()));
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("same-origin")));
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("cross-origin")));
EXPECT_FALSE(ExecJs(GetFrameByName("same-origin"), kCallWorkerScript));
ASSERT_EQ(AreAllSitesIsolatedForTesting() ? 2 : 1,
RenderProcessHost::GetCurrentRenderProcessCountForTesting());
NavigateViaRenderer(shell()->web_contents(),
GURL("https://other.test/notrial.html"));
EXPECT_FALSE(HasTrialEnabled(GetPrimaryMainFrame()));
}
IN_PROC_BROWSER_TEST_P(ForceEnabledOriginTrialsBrowserTest,
ForceEnabledOriginTrials_IframeInPage) {
set_url_to_enable_trial(main_url_.Resolve("/notrial.html"));
Observe(shell()->web_contents());
EXPECT_TRUE(NavigateToURL(shell(), main_url_));
EXPECT_FALSE(HasTrialEnabled(GetPrimaryMainFrame()));
EXPECT_TRUE(HasTrialEnabled(GetFrameByName("same-origin")));
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("cross-origin")));
EXPECT_TRUE(ExecJs(GetFrameByName("same-origin"), kCallWorkerScript));
const GURL url("https://other.test/notrial.html");
TestNavigationObserver navigation_observer(url);
navigation_observer.WatchExistingWebContents();
ASSERT_TRUE(ExecJs(GetFrameByName("same-origin"),
content::JsReplace("location.href=$1", url.spec())));
navigation_observer.WaitForNavigationFinished();
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("same-origin")));
}
IN_PROC_BROWSER_TEST_P(ForceEnabledOriginTrialsBrowserTest,
ForceEnabledOriginTrials_InjectedIframe) {
const GURL frame_url("https://newly-loaded.test/notrial.html");
set_url_to_enable_trial(frame_url);
Observe(shell()->web_contents());
EXPECT_TRUE(NavigateToURL(shell(), main_url_));
EXPECT_FALSE(HasTrialEnabled(GetPrimaryMainFrame()));
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("same-origin")));
EXPECT_FALSE(HasTrialEnabled(GetFrameByName("cross-origin")));
EXPECT_FALSE(ExecJs(GetFrameByName("same-origin"), kCallWorkerScript));
TestNavigationObserver navigation_observer(frame_url);
navigation_observer.WatchExistingWebContents();
ASSERT_TRUE(ExecJs(
GetFrameByName("same-origin"),
content::JsReplace("{"
" const ifrm = document.createElement('iframe');"
" ifrm.name='new-frame';"
" ifrm.src=$1;"
" document.body.appendChild(ifrm);"
"}",
frame_url.spec())));
navigation_observer.WaitForNavigationFinished();
EXPECT_TRUE(HasTrialEnabled(GetFrameByName("new-frame")));
}
INSTANTIATE_TEST_SUITE_P(SiteIsolation,
ForceEnabledOriginTrialsBrowserTest,
::testing::Bool());
}