blob: b83d17f10300be3a67c65f6df771c5c236f5e646 [file] [log] [blame]
// Copyright 2019 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/sync/sync_encryption_keys_tab_helper.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/test_signin_client_builder.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/common/sync_encryption_keys_extension.mojom.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/site_isolation/features.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "google_apis/gaia/gaia_urls.h"
#include "net/base/net_errors.h"
#include "net/http/http_response_headers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace {
class SyncEncryptionKeysTabHelperTest : public ChromeRenderViewHostTestHarness {
public:
SyncEncryptionKeysTabHelperTest() {
// Avoid the disabling of site isolation due to memory constraints, required
// on Android so that ApplyGlobalIsolatedOrigins() takes effect regardless
// of available memory when running the test (otherwise low-memory bots may
// run into test failures).
feature_list_.InitAndEnableFeatureWithParameters(
site_isolation::features::kSiteIsolationMemoryThresholds,
{{site_isolation::features::
kStrictSiteIsolationMemoryThresholdParamName,
"0"},
{site_isolation::features::
kPartialSiteIsolationMemoryThresholdParamName,
"0"}});
}
~SyncEncryptionKeysTabHelperTest() override = default;
SyncEncryptionKeysTabHelperTest(const SyncEncryptionKeysTabHelperTest&) =
delete;
SyncEncryptionKeysTabHelperTest& operator=(
const SyncEncryptionKeysTabHelperTest&) = delete;
protected:
// content::RenderViewHostTestHarness:
void SetUp() override {
content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins();
ChromeRenderViewHostTestHarness::SetUp();
SyncEncryptionKeysTabHelper::CreateForWebContents(web_contents());
}
void TearDown() override {
ChromeRenderViewHostTestHarness::TearDown();
// Undo content::SiteIsolationPolicy::ApplyGlobalIsolatedOrigins().
content::ChildProcessSecurityPolicy::GetInstance()
->ClearIsolatedOriginsForTesting();
}
bool HasEncryptionKeysApi(content::RenderFrameHost* rfh) {
auto* tab_helper =
SyncEncryptionKeysTabHelper::FromWebContents(web_contents());
return tab_helper->HasEncryptionKeysApiForTesting(rfh);
}
bool HasEncryptionKeysApiInMainFrame() {
return HasEncryptionKeysApi(main_rfh());
}
content::WebContentsTester* web_contents_tester() {
return content::WebContentsTester::For(web_contents());
}
TestingProfile::TestingFactories GetTestingFactories() const override {
return {{SyncServiceFactory::GetInstance(),
SyncServiceFactory::GetDefaultFactory()},
{ChromeSigninClientFactory::GetInstance(),
base::BindRepeating(&signin::BuildTestSigninClient)}};
}
base::test::ScopedFeatureList feature_list_;
};
TEST_F(SyncEncryptionKeysTabHelperTest, ShouldExposeMojoApiToAllowedOrigin) {
ASSERT_FALSE(HasEncryptionKeysApiInMainFrame());
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
EXPECT_TRUE(HasEncryptionKeysApiInMainFrame());
}
TEST_F(SyncEncryptionKeysTabHelperTest,
ShouldNotExposeMojoApiToUnallowedOrigin) {
web_contents_tester()->NavigateAndCommit(GURL("http://page.com"));
EXPECT_FALSE(HasEncryptionKeysApiInMainFrame());
}
// TODO(https://crbug.com/1394191): flaky on android bots.
TEST_F(SyncEncryptionKeysTabHelperTest,
DISABLED_ShouldNotExposeMojoApiIfNavigatedAway) {
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
ASSERT_TRUE(HasEncryptionKeysApiInMainFrame());
web_contents_tester()->NavigateAndCommit(GURL("http://page.com"));
EXPECT_FALSE(HasEncryptionKeysApiInMainFrame());
}
TEST_F(SyncEncryptionKeysTabHelperTest,
ShouldExposeMojoApiEvenIfSubframeNavigatedAway) {
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
content::RenderFrameHost* subframe =
content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
ASSERT_TRUE(HasEncryptionKeysApiInMainFrame());
content::NavigationSimulator::CreateRendererInitiated(GURL("http://page.com"),
subframe)
->Commit();
// For the receiver set to be fully updated, a mainframe navigation is needed.
// Otherwise the test passes regardless of whether the logic is buggy.
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
EXPECT_TRUE(HasEncryptionKeysApiInMainFrame());
}
TEST_F(SyncEncryptionKeysTabHelperTest,
ShouldNotExposeMojoApiIfNavigationFailed) {
auto* render_frame_host =
content::NavigationSimulator::NavigateAndFailFromBrowser(
web_contents(), GaiaUrls::GetInstance()->gaia_url(),
net::ERR_ABORTED);
EXPECT_FALSE(HasEncryptionKeysApi(render_frame_host));
EXPECT_FALSE(HasEncryptionKeysApiInMainFrame());
}
// TODO(https://crbug.com/1394191): flaky on android bots.
TEST_F(SyncEncryptionKeysTabHelperTest,
DISABLED_ShouldNotExposeMojoApiIfNavigatedAwayToErrorPage) {
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
ASSERT_TRUE(HasEncryptionKeysApiInMainFrame());
auto* render_frame_host =
content::NavigationSimulator::NavigateAndFailFromBrowser(
web_contents(), GURL("http://page.com"), net::ERR_ABORTED);
EXPECT_FALSE(HasEncryptionKeysApi(render_frame_host));
// `net::ERR_ABORTED` doesn't update the main frame and the previous main
// frame is still available. So, EncryptionKeysApi is still valid on the main
// frame.
EXPECT_TRUE(HasEncryptionKeysApiInMainFrame());
render_frame_host = content::NavigationSimulator::NavigateAndFailFromBrowser(
web_contents(), GURL("http://page.com"), net::ERR_TIMED_OUT);
EXPECT_FALSE(HasEncryptionKeysApi(render_frame_host));
// `net::ERR_TIMED_OUT` commits the error page that is the main frame now.
EXPECT_FALSE(HasEncryptionKeysApiInMainFrame());
}
class SyncEncryptionKeysTabHelperPrerenderingTest
: public SyncEncryptionKeysTabHelperTest {
public:
SyncEncryptionKeysTabHelperPrerenderingTest() = default;
private:
content::test::ScopedPrerenderFeatureList prerender_feature_list_;
};
// Tests that EncryptionKeys works based on a main frame. A prerendered page
// also creates EncryptionKeys as it's a main frame. But, if EncryptionKeys
// is accessed thrugh Mojo in prerendering, it causes canceling prerendering.
// See the browser test, 'ShouldNotBindEncryptionKeysApiInPrerendering', from
// 'sync_encryption_keys_tab_helper_browsertest.cc' for the details about
// canceling prerendering.
TEST_F(SyncEncryptionKeysTabHelperPrerenderingTest,
CreateEncryptionKeysInPrerendering) {
content::test::ScopedPrerenderWebContentsDelegate web_contents_delegate(
*web_contents());
// Load a page.
ASSERT_FALSE(HasEncryptionKeysApiInMainFrame());
web_contents_tester()->NavigateAndCommit(GaiaUrls::GetInstance()->gaia_url());
ASSERT_TRUE(HasEncryptionKeysApiInMainFrame());
// If prerendering happens in the gaia url, EncryptionKeys is created for
// the prerendering and the current EncryptionKeys for a primary page also
// exists.
const GURL kPrerenderingUrl(GaiaUrls::GetInstance()->gaia_url().spec() +
"?prerendering");
auto* prerender_rfh = content::WebContentsTester::For(web_contents())
->AddPrerenderAndCommitNavigation(kPrerenderingUrl);
DCHECK_NE(prerender_rfh, nullptr);
EXPECT_TRUE(HasEncryptionKeysApi(prerender_rfh));
EXPECT_TRUE(HasEncryptionKeysApiInMainFrame());
content::test::PrerenderHostObserver host_observer(*web_contents(),
kPrerenderingUrl);
// Activate the prerendered page.
auto* activated_rfh =
content::NavigationSimulator::NavigateAndCommitFromDocument(
kPrerenderingUrl, web_contents()->GetPrimaryMainFrame());
host_observer.WaitForActivation();
EXPECT_TRUE(host_observer.was_activated());
// The EncryptionKeys exists for the activated page.
EXPECT_TRUE(HasEncryptionKeysApi(activated_rfh));
// If the prerendering happens to the cross origin, the prerendering would be
// canceled.
const GURL kCrossOriginPrerenderingUrl(GURL("http://page.com"));
int frame_tree_node_id = content::WebContentsTester::For(web_contents())
->AddPrerender(kCrossOriginPrerenderingUrl);
ASSERT_EQ(frame_tree_node_id, content::RenderFrameHost::kNoFrameTreeNodeId);
// EncryptionKeys is still valid in a primary page.
EXPECT_TRUE(HasEncryptionKeysApiInMainFrame());
// Load a page that doesn't allow EncryptionKeys.
auto* rfh = content::NavigationSimulator::NavigateAndCommitFromDocument(
kCrossOriginPrerenderingUrl, web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(HasEncryptionKeysApi(rfh));
EXPECT_FALSE(HasEncryptionKeysApiInMainFrame());
}
} // namespace