chromium/android_webview/browser/aw_client_hints_controller_delegate_unittest.cc

// 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