| // 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 "android_webview/browser/aw_client_hints_controller_delegate.h" |
| |
| #include "base/memory/ref_counted.h" |
| #include "components/embedder_support/user_agent_utils.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace android_webview { |
| |
| namespace { |
| class PermissiveAwClientHintsControllerDelegate |
| : public AwClientHintsControllerDelegate { |
| public: |
| explicit PermissiveAwClientHintsControllerDelegate(PrefService* pref_service) |
| : AwClientHintsControllerDelegate(pref_service) {} |
| |
| bool IsJavaScriptAllowed(const GURL& url, |
| content::RenderFrameHost* parent_rfh) override { |
| // We need to be permissive here to test persisting client hints. |
| return true; |
| } |
| }; |
| } // namespace |
| |
| class AwClientHintsControllerDelegateTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| prefs_ = std::make_unique<TestingPrefServiceSimple>(); |
| prefs_->registry()->RegisterDictionaryPref( |
| prefs::kClientHintsCachedPerOriginMap); |
| client_hints_controller_delegate_ = |
| std::make_unique<AwClientHintsControllerDelegate>(prefs_.get()); |
| permissive_client_hints_controller_delegate_ = |
| std::make_unique<PermissiveAwClientHintsControllerDelegate>( |
| prefs_.get()); |
| } |
| |
| std::unique_ptr<content::ClientHintsControllerDelegate> |
| client_hints_controller_delegate_; |
| std::unique_ptr<content::ClientHintsControllerDelegate> |
| permissive_client_hints_controller_delegate_; |
| std::unique_ptr<TestingPrefServiceSimple> prefs_; |
| }; |
| |
| TEST_F(AwClientHintsControllerDelegateTest, GetNetworkQualityTracker) { |
| EXPECT_EQ(nullptr, |
| client_hints_controller_delegate_->GetNetworkQualityTracker()); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, GetAllowedClientHintsFromSource) { |
| // Verify empty Origin is rejected even if additional hints are set. |
| blink::EnabledClientHints enabled_hints; |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin(), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| permissive_client_hints_controller_delegate_->SetAdditionalClientHints( |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin(), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| permissive_client_hints_controller_delegate_->ClearAdditionalClientHints(); |
| |
| // Verify insecure Origin is rejected even if additional hints are set. |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| permissive_client_hints_controller_delegate_->SetAdditionalClientHints( |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("http://example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| permissive_client_hints_controller_delegate_->ClearAdditionalClientHints(); |
| |
| // Verify persisted and additional hints are combined. |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->SetAdditionalClientHints( |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin::Create(GURL("https://example.com")), nullptr, |
| {network::mojom::WebClientHintsType::kPrefersColorScheme}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_EQ( |
| enabled_hints.GetEnabledHints(), |
| std::vector({network::mojom::WebClientHintsType::kPrefersColorScheme, |
| network::mojom::WebClientHintsType::kDeviceMemory})); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, IsJavaScriptAllowed) { |
| EXPECT_TRUE(client_hints_controller_delegate_->IsJavaScriptAllowed(GURL(""), |
| nullptr)); |
| EXPECT_TRUE(client_hints_controller_delegate_->IsJavaScriptAllowed( |
| GURL("https://example.com/"), nullptr)); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, AreThirdPartyCookiesBlocked) { |
| EXPECT_FALSE(client_hints_controller_delegate_->AreThirdPartyCookiesBlocked( |
| GURL(""), nullptr)); |
| EXPECT_FALSE(client_hints_controller_delegate_->AreThirdPartyCookiesBlocked( |
| GURL("https://example.com"), nullptr)); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, GetUserAgentMetadata) { |
| auto metadata = client_hints_controller_delegate_->GetUserAgentMetadata(); |
| |
| // Most fields should match those from the embedder_support util function. |
| auto from_embedder = embedder_support::GetUserAgentMetadata(prefs_.get()); |
| |
| EXPECT_EQ(metadata.architecture, from_embedder.architecture); |
| EXPECT_EQ(metadata.bitness, from_embedder.bitness); |
| EXPECT_EQ(metadata.full_version, from_embedder.full_version); |
| EXPECT_EQ(metadata.mobile, from_embedder.mobile); |
| EXPECT_EQ(metadata.model, from_embedder.model); |
| EXPECT_EQ(metadata.platform, from_embedder.platform); |
| EXPECT_EQ(metadata.platform_version, from_embedder.platform_version); |
| EXPECT_EQ(metadata.wow64, from_embedder.wow64); |
| |
| // The brand version lists should contain Android Webview. |
| for (auto& list : |
| {metadata.brand_version_list, metadata.brand_full_version_list}) { |
| EXPECT_THAT(list, testing::Contains(testing::Field( |
| &blink::UserAgentBrandVersion::brand, |
| testing::Eq(kAndroidWebViewProductName)))); |
| } |
| |
| // Verify only generate low-entropy client hints. |
| metadata = AwClientHintsControllerDelegate::GetUserAgentMetadataOverrideBrand( |
| /*only_low_entropy_ch=*/true); |
| EXPECT_THAT(metadata.brand_version_list, |
| testing::Contains( |
| testing::Field(&blink::UserAgentBrandVersion::brand, |
| testing::Eq(kAndroidWebViewProductName)))); |
| EXPECT_EQ("Android", metadata.platform); |
| |
| // No high entropy client hints. |
| EXPECT_TRUE(metadata.full_version.empty()); |
| EXPECT_TRUE(metadata.brand_full_version_list.empty()); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, PersistClientHints) { |
| // Empty origin can't persist hints. |
| blink::EnabledClientHints enabled_hints; |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin(), nullptr, |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin(), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| |
| // Insecure origin can't persist hints. |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin::Create(GURL("http://example.com")), nullptr, |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("http://example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| |
| // Persisting hints for one origin doesnt affect others. |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin::Create(GURL("https://example.com")), nullptr, |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_EQ(enabled_hints.GetEnabledHints(), |
| std::vector({network::mojom::WebClientHintsType::kDeviceMemory})); |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://another.example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| |
| // Persisted hints can be cleared. |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin::Create(GURL("https://example.com")), nullptr, |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_EQ(enabled_hints.GetEnabledHints(), |
| std::vector({network::mojom::WebClientHintsType::kDeviceMemory})); |
| enabled_hints = blink::EnabledClientHints(); |
| permissive_client_hints_controller_delegate_->PersistClientHints( |
| url::Origin::Create(GURL("https://example.com")), nullptr, {}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, SetAdditionalClientHints) { |
| blink::EnabledClientHints enabled_hints; |
| permissive_client_hints_controller_delegate_->SetAdditionalClientHints( |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_EQ(enabled_hints.GetEnabledHints(), |
| std::vector({network::mojom::WebClientHintsType::kDeviceMemory})); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, ClearAdditionalClientHints) { |
| blink::EnabledClientHints enabled_hints; |
| permissive_client_hints_controller_delegate_->SetAdditionalClientHints( |
| {network::mojom::WebClientHintsType::kDeviceMemory}); |
| permissive_client_hints_controller_delegate_->ClearAdditionalClientHints(); |
| permissive_client_hints_controller_delegate_->GetAllowedClientHintsFromSource( |
| url::Origin::Create(GURL("https://example.com")), &enabled_hints); |
| EXPECT_TRUE(enabled_hints.GetEnabledHints().empty()); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, |
| SetMostRecentMainFrameViewportSize) { |
| client_hints_controller_delegate_->SetMostRecentMainFrameViewportSize( |
| gfx::Size(1, 1)); |
| EXPECT_EQ( |
| gfx::Size(1, 1), |
| client_hints_controller_delegate_->GetMostRecentMainFrameViewportSize()); |
| } |
| |
| TEST_F(AwClientHintsControllerDelegateTest, |
| GetMostRecentMainFrameViewportSize) { |
| EXPECT_EQ( |
| gfx::Size(0, 0), |
| client_hints_controller_delegate_->GetMostRecentMainFrameViewportSize()); |
| } |
| |
| } // namespace android_webview |