// Copyright 2015 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/usb/usb_chooser_context.h"

#include <vector>

#include "base/containers/flat_map.h"
#include "base/functional/callback_helpers.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/usb/usb_chooser_context_factory.h"
#include "chrome/browser/usb/usb_chooser_context_mock_device_observer.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/permissions/test/object_permission_context_base_mock_permission_observer.h"
#include "components/prefs/pref_service.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "services/device/public/cpp/test/fake_usb_device_manager.h"
#include "services/device/public/mojom/usb_device.mojom.h"
#include "services/device/public/mojom/usb_manager.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#endif

using ::base::test::TestFuture;
using ::content_settings::SettingSource;
using ::device::mojom::UsbDeviceInfoPtr;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::NiceMock;

namespace {

constexpr char kDeviceNameKey[] = "name";
constexpr char kGuidKey[] = "ephemeral-guid";
constexpr char kProductIdKey[] = "product-id";
constexpr char kSerialNumberKey[] = "serial-number";
constexpr char kVendorIdKey[] = "vendor-id";
constexpr int kDeviceIdWildcard = -1;

class UsbChooserContextTest : public testing::Test {
 public:
  UsbChooserContextTest() {
#if BUILDFLAG(IS_CHROMEOS)
    profile_.ScopedCrosSettingsTestHelper()
        ->ReplaceDeviceSettingsProviderWithStub();
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

  ~UsbChooserContextTest() override {
    // When UsbChooserContext is destroyed, OnDeviceManagerConnectionError will
    // be called for each device observer.
    for (const auto& entry : mock_device_observers_) {
      EXPECT_CALL(*entry.second, OnDeviceManagerConnectionError)
          .Times(AnyNumber());
    }

    // OnPermissionRevoked and OnObjectPermissionChanged will be called for any
    // ephemeral device permissions that are active.
    for (const auto& entry : mock_permission_observers_) {
      EXPECT_CALL(*entry.second, OnPermissionRevoked).Times(AnyNumber());
      EXPECT_CALL(*entry.second, OnObjectPermissionChanged).Times(AnyNumber());
    }

#if BUILDFLAG(IS_CHROMEOS)
    profile_.ScopedCrosSettingsTestHelper()
        ->RestoreRealDeviceSettingsProvider();
#endif  // BUILDFLAG(IS_CHROMEOS)
  }

 protected:
  TestingProfile* profile() { return &profile_; }

  UsbChooserContext* GetChooserContext(Profile* profile) {
    auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile);
    mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
    device_manager_.AddReceiver(
        device_manager.InitWithNewPipeAndPassReceiver());
    chooser_context->SetDeviceManagerForTesting(std::move(device_manager));

    // Call GetDevices once to make sure the connection with DeviceManager has
    // been set up, so that it can be notified when device is removed.
    TestFuture<std::vector<device::mojom::UsbDeviceInfoPtr>> devices_future;
    chooser_context->GetDevices(devices_future.GetCallback());
    EXPECT_TRUE(devices_future.Wait());

    // Add observers
    EXPECT_FALSE(mock_permission_observers_.contains(profile));
    EXPECT_FALSE(mock_device_observers_.contains(profile));
    mock_permission_observers_.emplace(
        profile,
        std::make_unique<NiceMock<permissions::MockPermissionObserver>>());
    mock_device_observers_.emplace(profile,
                                   std::make_unique<MockDeviceObserver>());
    NiceMock<permissions::MockPermissionObserver>* permission_observer =
        mock_permission_observers_[profile].get();
    chooser_context->permissions::ObjectPermissionContextBase::AddObserver(
        permission_observer);
    MockDeviceObserver* device_observer = mock_device_observers_[profile].get();
    chooser_context->AddObserver(device_observer);
    EXPECT_CALL(*device_observer, OnBrowserContextShutdown)
        .WillOnce([chooser_context, permission_observer, device_observer]() {
          chooser_context
              ->permissions::ObjectPermissionContextBase::RemoveObserver(
                  permission_observer);
          chooser_context->RemoveObserver(device_observer);
        });
    return chooser_context;
  }

  device::FakeUsbDeviceManager device_manager_;

  // Mock observers
  base::flat_map<Profile*,
                 std::unique_ptr<NiceMock<permissions::MockPermissionObserver>>>
      mock_permission_observers_;
  base::flat_map<Profile*, std::unique_ptr<MockDeviceObserver>>
      mock_device_observers_;

 private:
  content::BrowserTaskEnvironment task_environment_;
  TestingProfile profile_;
};

}  // namespace

TEST_F(UsbChooserContextTest, CheckGrantAndRevokePermission) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC");
  UsbChooserContext* store = GetChooserContext(profile());

  auto object = base::Value(base::DictValue()
                                .Set(kDeviceNameKey, "Gizmo")
                                .Set(kVendorIdKey, 0)
                                .Set(kProductIdKey, 0)
                                .Set(kSerialNumberKey, "123ABC"));

  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  store->GrantDevicePermission(origin, *device_info);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info));
  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(origin);
  ASSERT_EQ(1u, objects.size());
  EXPECT_EQ(object, objects[0]->value);

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  ASSERT_EQ(1u, all_origin_objects.size());
  EXPECT_EQ(url, all_origin_objects[0]->origin);
  EXPECT_EQ(object, all_origin_objects[0]->value);
  EXPECT_FALSE(all_origin_objects[0]->incognito);

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnPermissionRevoked(origin));

  store->RevokeObjectPermission(origin, objects[0]->value);
  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));

  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(0u, objects.size());

  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(0u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, CheckGrantAndRevokeEphemeralPermission) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");
  UsbDeviceInfoPtr other_device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");

  UsbChooserContext* store = GetChooserContext(profile());

  auto object = base::Value(
      base::DictValue()
          .Set(kDeviceNameKey, "Gizmo")
          .Set(kGuidKey, device_info->guid)
          .Set(kVendorIdKey, static_cast<int>(device_info->vendor_id))
          .Set(kProductIdKey, static_cast<int>(device_info->product_id)));

  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  store->GrantDevicePermission(origin, *device_info);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info));
  EXPECT_FALSE(store->HasDevicePermission(origin, *other_device_info));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(origin);
  EXPECT_EQ(1u, objects.size());
  EXPECT_EQ(object, objects[0]->value);

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());
  EXPECT_EQ(url, all_origin_objects[0]->origin);
  EXPECT_EQ(object, all_origin_objects[0]->value);
  EXPECT_FALSE(all_origin_objects[0]->incognito);

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnPermissionRevoked(origin));

  store->RevokeObjectPermission(origin, objects[0]->value);
  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));

  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(0u, objects.size());
  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(0u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, DisconnectDeviceWithPermission) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC");

  UsbChooserContext* store = GetChooserContext(profile());

  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  store->GrantDevicePermission(origin, *device_info);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(origin);
  EXPECT_EQ(1u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());

  EXPECT_CALL(*mock_device_observers_[profile()], OnDeviceRemoved(_));
  device_manager_.RemoveDevice(device_info->guid);
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info));
  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(1u, objects.size());
  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());

  UsbDeviceInfoPtr reconnected_device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC");

  EXPECT_TRUE(store->HasDevicePermission(origin, *reconnected_device_info));
  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(1u, objects.size());
  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, DisconnectDeviceWithEphemeralPermission) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");

  UsbChooserContext* store = GetChooserContext(profile());

  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  store->GrantDevicePermission(origin, *device_info);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(origin);
  EXPECT_EQ(1u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  EXPECT_CALL(*mock_device_observers_[profile()], OnDeviceRemoved(_));
  device_manager_.RemoveDevice(device_info->guid);
  base::RunLoop().RunUntilIdle();

  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(0u, objects.size());
  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(0u, all_origin_objects.size());

  UsbDeviceInfoPtr reconnected_device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");

  EXPECT_FALSE(store->HasDevicePermission(origin, *reconnected_device_info));
  objects = store->GetGrantedObjects(origin);
  EXPECT_EQ(0u, objects.size());
  all_origin_objects = store->GetAllGrantedObjects();
  EXPECT_EQ(0u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, GrantPermissionInIncognito) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);
  UsbDeviceInfoPtr device_info_1 =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");
  UsbDeviceInfoPtr device_info_2 =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");
  UsbChooserContext* store = GetChooserContext(profile());
  auto* otr_profile =
      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  UsbChooserContext* incognito_store = GetChooserContext(otr_profile);

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  store->GrantDevicePermission(origin, *device_info_1);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info_1));
  EXPECT_FALSE(incognito_store->HasDevicePermission(origin, *device_info_1));

  EXPECT_CALL(*mock_permission_observers_[otr_profile],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));

  incognito_store->GrantDevicePermission(origin, *device_info_2);
  EXPECT_TRUE(store->HasDevicePermission(origin, *device_info_1));
  EXPECT_FALSE(store->HasDevicePermission(origin, *device_info_2));
  EXPECT_FALSE(incognito_store->HasDevicePermission(origin, *device_info_1));
  EXPECT_TRUE(incognito_store->HasDevicePermission(origin, *device_info_2));

  {
    std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
        store->GetGrantedObjects(origin);
    EXPECT_EQ(1u, objects.size());
    std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
        store->GetAllGrantedObjects();
    ASSERT_EQ(1u, all_origin_objects.size());
    EXPECT_FALSE(all_origin_objects[0]->incognito);
  }
  {
    std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
        incognito_store->GetGrantedObjects(origin);
    EXPECT_EQ(1u, objects.size());
    std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
        incognito_store->GetAllGrantedObjects();
    ASSERT_EQ(1u, all_origin_objects.size());
    EXPECT_TRUE(all_origin_objects[0]->incognito);
  }
}

TEST_F(UsbChooserContextTest, UsbGuardPermission) {
  const GURL kFooUrl("https://foo.com");
  const auto kFooOrigin = url::Origin::Create(kFooUrl);
  const GURL kBarUrl("https://bar.com");
  const auto kBarOrigin = url::Origin::Create(kBarUrl);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "ABC123");
  UsbDeviceInfoPtr ephemeral_device_info =
      device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");

  auto* map = HostContentSettingsMapFactory::GetForProfile(profile());
  map->SetContentSettingDefaultScope(
      kFooUrl, kFooUrl, ContentSettingsType::USB_GUARD, CONTENT_SETTING_BLOCK);

  auto* store = GetChooserContext(profile());
  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA))
      .Times(4);
  store->GrantDevicePermission(kFooOrigin, *device_info);
  store->GrantDevicePermission(kFooOrigin, *ephemeral_device_info);
  store->GrantDevicePermission(kBarOrigin, *device_info);
  store->GrantDevicePermission(kBarOrigin, *ephemeral_device_info);

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(kFooOrigin);
  EXPECT_EQ(0u, objects.size());

  objects = store->GetGrantedObjects(kBarOrigin);
  EXPECT_EQ(2u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  for (const auto& object : all_origin_objects) {
    EXPECT_EQ(object->origin, kBarUrl);
  }
  EXPECT_EQ(2u, all_origin_objects.size());

  EXPECT_FALSE(store->HasDevicePermission(kFooOrigin, *device_info));
  EXPECT_FALSE(store->HasDevicePermission(kFooOrigin, *ephemeral_device_info));
  EXPECT_TRUE(store->HasDevicePermission(kBarOrigin, *device_info));
  EXPECT_TRUE(store->HasDevicePermission(kBarOrigin, *ephemeral_device_info));
}

TEST_F(UsbChooserContextTest, GetObjectDisplayNameForNamelessDevice) {
  const GURL kGoogleUrl("https://www.google.com");
  const auto kGoogleOrigin = url::Origin::Create(kGoogleUrl);
  UsbDeviceInfoPtr device_info =
      device_manager_.CreateAndAddDevice(6353, 5678, "", "", "");

  auto* store = GetChooserContext(profile());
  store->GrantDevicePermission(kGoogleOrigin, *device_info);

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 1u);
  EXPECT_EQ(store->GetObjectDisplayName(objects[0]->value),
            u"Unknown product 0x162E from Google Inc.");
}

TEST_F(UsbChooserContextTest, PolicyGuardPermission) {
  const auto origin = url::Origin::Create(GURL("https://google.com"));

  UsbDeviceInfoPtr device =
      device_manager_.CreateAndAddDevice(0, 0, "", "", "");
  auto* store = GetChooserContext(profile());
  store->GrantDevicePermission(origin, *device);

  auto* prefs = profile()->GetTestingPrefService();
  prefs->SetManagedPref(prefs::kManagedDefaultWebUsbGuardSetting,
                        std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
  EXPECT_FALSE(store->CanRequestObjectPermission(origin));
  EXPECT_FALSE(store->HasDevicePermission(origin, *device));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(origin);
  EXPECT_EQ(0u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(0u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, PolicyAskForUrls) {
  const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
  const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));

  UsbDeviceInfoPtr device =
      device_manager_.CreateAndAddDevice(0, 0, "", "", "");
  auto* store = GetChooserContext(profile());
  store->GrantDevicePermission(kFooOrigin, *device);
  store->GrantDevicePermission(kBarOrigin, *device);

  // Set the default to "ask" so that the policy being tested overrides it.
  auto* prefs = profile()->GetTestingPrefService();
  prefs->SetManagedPref(prefs::kManagedDefaultWebUsbGuardSetting,
                        std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
  prefs->SetManagedPref(prefs::kManagedWebUsbAskForUrls,
                        base::test::ParseJsonList(R"(
    [ "https://foo.origin" ]
  )"));

  EXPECT_TRUE(store->CanRequestObjectPermission(kFooOrigin));
  EXPECT_TRUE(store->HasDevicePermission(kFooOrigin, *device));
  EXPECT_FALSE(store->CanRequestObjectPermission(kBarOrigin));
  EXPECT_FALSE(store->HasDevicePermission(kBarOrigin, *device));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(kFooOrigin);
  EXPECT_EQ(1u, objects.size());
  objects = store->GetGrantedObjects(kBarOrigin);
  EXPECT_EQ(0u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());
}

TEST_F(UsbChooserContextTest, PolicyBlockedForUrls) {
  const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
  const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));

  UsbDeviceInfoPtr device =
      device_manager_.CreateAndAddDevice(0, 0, "", "", "");
  auto* store = GetChooserContext(profile());
  store->GrantDevicePermission(kFooOrigin, *device);
  store->GrantDevicePermission(kBarOrigin, *device);

  auto* prefs = profile()->GetTestingPrefService();
  prefs->SetManagedPref(prefs::kManagedWebUsbBlockedForUrls,
                        base::test::ParseJsonList(R"(
    [ "https://foo.origin" ]
  )"));

  EXPECT_FALSE(store->CanRequestObjectPermission(kFooOrigin));
  EXPECT_FALSE(store->HasDevicePermission(kFooOrigin, *device));
  EXPECT_TRUE(store->CanRequestObjectPermission(kBarOrigin));
  EXPECT_TRUE(store->HasDevicePermission(kBarOrigin, *device));

  std::vector<std::unique_ptr<UsbChooserContext::Object>> objects =
      store->GetGrantedObjects(kFooOrigin);
  EXPECT_EQ(0u, objects.size());
  objects = store->GetGrantedObjects(kBarOrigin);
  EXPECT_EQ(1u, objects.size());

  std::vector<std::unique_ptr<UsbChooserContext::Object>> all_origin_objects =
      store->GetAllGrantedObjects();
  EXPECT_EQ(1u, all_origin_objects.size());
}

namespace {

constexpr char kPolicySetting[] = R"(
    [
      {
        "devices": [{ "vendor_id": 6353, "product_id": 5678 }],
        "urls": ["https://product.vendor.com"]
      }, {
        "devices": [{ "vendor_id": 6353 }],
        "urls": ["https://vendor.com"]
      }, {
        "devices": [{}],
        "urls": ["https://anydevice.com"]
      }, {
        "devices": [{ "vendor_id": 6354, "product_id": 1357 }],
        "urls": ["https://gadget.com,https://cool.com"]
      }
    ])";

// Test URLs
constexpr char kAnyDeviceUrl[] = "https://anydevice.com/";
constexpr char kVendorUrl[] = "https://vendor.com/";
constexpr char kProductVendorUrl[] = "https://product.vendor.com/";
constexpr char kGadgetUrl[] = "https://gadget.com/";
constexpr char kCoolUrl[] = "https://cool.com/";

const std::vector<GURL>& PolicyOrigins() {
  static base::NoDestructor<std::vector<GURL>> origins{
      {GURL(kProductVendorUrl), GURL(kVendorUrl), GURL(kAnyDeviceUrl),
       GURL(kGadgetUrl), GURL(kCoolUrl)}};
  return *origins;
}

void ExpectNoPermissions(UsbChooserContext* store,
                         const device::mojom::UsbDeviceInfo& device_info) {
  for (const auto& origin_url : PolicyOrigins()) {
    const auto origin = url::Origin::Create(origin_url);
    EXPECT_FALSE(store->HasDevicePermission(origin, device_info));
  }
}

void ExpectCorrectPermissions(UsbChooserContext* store,
                              const std::vector<GURL>& kValidOrigins,
                              const std::vector<GURL>& kInvalidOrigins,
                              const device::mojom::UsbDeviceInfo& device_info) {
  // Ensure that only |kValidOrigin| as the top-level origin has
  // permission to access the device described by |device_info|.
  for (const auto& valid_origin : kValidOrigins) {
    EXPECT_TRUE(store->HasDevicePermission(url::Origin::Create(valid_origin),
                                           device_info));
  }

  for (const auto& invalid_origin : kInvalidOrigins) {
    EXPECT_FALSE(store->HasDevicePermission(url::Origin::Create(invalid_origin),
                                            device_info));
  }
}

}  // namespace

TEST_F(UsbChooserContextTest,
       UsbAllowDevicesForUrlsPermissionForSpecificDevice) {
  const std::vector<GURL> kValidOrigins = {
      GURL(kProductVendorUrl), GURL(kVendorUrl), GURL(kAnyDeviceUrl)};
  const std::vector<GURL> kInvalidOrigins = {GURL(kGadgetUrl), GURL(kCoolUrl)};

  UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Google", "Gizmo", "ABC123");

  auto* store = GetChooserContext(profile());

  ExpectNoPermissions(store, *specific_device_info);

  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  ExpectCorrectPermissions(store, kValidOrigins, kInvalidOrigins,
                           *specific_device_info);
}

TEST_F(UsbChooserContextTest,
       UsbAllowDevicesForUrlsPermissionForVendorRelatedDevice) {
  const std::vector<GURL> kValidOrigins = {GURL(kVendorUrl),
                                           GURL(kAnyDeviceUrl)};
  const std::vector<GURL> kInvalidOrigins = {GURL(kProductVendorUrl),
                                             GURL(kGadgetUrl), GURL(kCoolUrl)};

  UsbDeviceInfoPtr vendor_related_device_info =
      device_manager_.CreateAndAddDevice(6353, 8765, "Google", "Widget",
                                         "XYZ987");

  auto* store = GetChooserContext(profile());

  ExpectNoPermissions(store, *vendor_related_device_info);

  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  ExpectCorrectPermissions(store, kValidOrigins, kInvalidOrigins,
                           *vendor_related_device_info);
}

TEST_F(UsbChooserContextTest,
       UsbAllowDevicesForUrlsPermissionForUnrelatedDevice) {
  const std::vector<GURL> kValidOrigins = {GURL(kAnyDeviceUrl)};
  const std::vector<GURL> kInvalidOrigins = {
      GURL(kProductVendorUrl), GURL(kVendorUrl), GURL(kGadgetUrl)};
  const auto kCoolOrigin = url::Origin::Create(GURL(kCoolUrl));

  UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice(
      6354, 1357, "Cool", "Gadget", "4W350M3");

  auto* store = GetChooserContext(profile());

  ExpectNoPermissions(store, *unrelated_device_info);

  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  EXPECT_TRUE(store->HasDevicePermission(kCoolOrigin, *unrelated_device_info));
  for (const auto& origin_url : PolicyOrigins()) {
    const auto origin = url::Origin::Create(origin_url);
    if (origin_url != GURL(kCoolUrl) && origin_url != GURL(kAnyDeviceUrl)) {
      EXPECT_FALSE(store->HasDevicePermission(origin, *unrelated_device_info));
    }
  }
  ExpectCorrectPermissions(store, kValidOrigins, kInvalidOrigins,
                           *unrelated_device_info);
}

TEST_F(UsbChooserContextTest,
       UsbAllowDevicesForUrlsPermissionOverrulesUsbGuardPermission) {
  const auto kProductVendorOrigin =
      url::Origin::Create(GURL(kProductVendorUrl));
  const auto kGadgetOrigin = url::Origin::Create(GURL(kGadgetUrl));
  const auto kCoolOrigin = url::Origin::Create(GURL(kCoolUrl));

  UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Google", "Gizmo", "ABC123");
  UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice(
      6354, 1357, "Cool", "Gadget", "4W350M3");

  auto* store = GetChooserContext(profile());

  ExpectNoPermissions(store, *specific_device_info);
  ExpectNoPermissions(store, *unrelated_device_info);

  auto* map = HostContentSettingsMapFactory::GetForProfile(profile());
  map->SetContentSettingDefaultScope(
      GURL(kProductVendorUrl), GURL(kProductVendorUrl),
      ContentSettingsType::USB_GUARD, CONTENT_SETTING_BLOCK);
  map->SetContentSettingDefaultScope(GURL(kGadgetUrl), GURL(kCoolUrl),
                                     ContentSettingsType::USB_GUARD,
                                     CONTENT_SETTING_BLOCK);
  EXPECT_FALSE(
      store->HasDevicePermission(kProductVendorOrigin, *specific_device_info));
  EXPECT_FALSE(
      store->HasDevicePermission(kProductVendorOrigin, *unrelated_device_info));
  EXPECT_FALSE(store->HasDevicePermission(kCoolOrigin, *specific_device_info));
  EXPECT_FALSE(store->HasDevicePermission(kCoolOrigin, *unrelated_device_info));

  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  EXPECT_TRUE(
      store->HasDevicePermission(kProductVendorOrigin, *specific_device_info));
  EXPECT_FALSE(
      store->HasDevicePermission(kProductVendorOrigin, *unrelated_device_info));
  EXPECT_FALSE(store->HasDevicePermission(kCoolOrigin, *specific_device_info));
  EXPECT_TRUE(store->HasDevicePermission(kCoolOrigin, *unrelated_device_info));
}

#if BUILDFLAG(IS_CHROMEOS)

class DeviceLoginScreenWebUsbChooserContextTest : public UsbChooserContextTest {
 public:
  DeviceLoginScreenWebUsbChooserContextTest() {
    TestingProfile::Builder builder;
    builder.SetPath(base::FilePath(FILE_PATH_LITERAL(chrome::kInitialProfile)));
    signin_profile_ = builder.Build();
  }
  ~DeviceLoginScreenWebUsbChooserContextTest() override = default;

 protected:
  Profile* GetSigninProfile() { return signin_profile_.get(); }

 private:
  std::unique_ptr<Profile> signin_profile_;
};

TEST_F(DeviceLoginScreenWebUsbChooserContextTest,
       UserUsbChooserContextOnlyUsesUserPolicy) {
  const std::vector<GURL> kValidOrigins = {
      GURL(kProductVendorUrl), GURL(kVendorUrl), GURL(kAnyDeviceUrl)};
  const std::vector<GURL> kInvalidOrigins = {GURL(kGadgetUrl), GURL(kCoolUrl)};

  UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Google", "Gizmo", "ABC123");

  Profile* user_profile = profile();
  Profile* signin_profile = GetSigninProfile();

  auto* user_store = GetChooserContext(user_profile);
  auto* signin_store = GetChooserContext(signin_profile);

  ExpectNoPermissions(user_store, *specific_device_info);
  ExpectNoPermissions(signin_store, *specific_device_info);

  user_profile->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                    base::test::ParseJsonList(kPolicySetting));

  ExpectCorrectPermissions(user_store, kValidOrigins, kInvalidOrigins,
                           *specific_device_info);
  ExpectNoPermissions(signin_store, *specific_device_info);
}

TEST_F(DeviceLoginScreenWebUsbChooserContextTest,
       SigninUsbChooserContextOnlyUsesDevicePolicy) {
  const std::vector<GURL> kValidOrigins = {
      GURL(kProductVendorUrl), GURL(kVendorUrl), GURL(kAnyDeviceUrl)};
  const std::vector<GURL> kInvalidOrigins = {GURL(kGadgetUrl), GURL(kCoolUrl)};

  UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Google", "Gizmo", "ABC123");

  Profile* user_profile = profile();
  Profile* signin_profile = GetSigninProfile();

  auto* user_store = GetChooserContext(user_profile);
  auto* signin_store = GetChooserContext(signin_profile);

  ExpectNoPermissions(user_store, *specific_device_info);
  ExpectNoPermissions(signin_store, *specific_device_info);

  signin_profile->GetPrefs()->SetList(
      prefs::kManagedWebUsbAllowDevicesForUrls,
      base::test::ParseJsonList(kPolicySetting));

  ExpectNoPermissions(user_store, *specific_device_info);
  ExpectCorrectPermissions(signin_store, kValidOrigins, kInvalidOrigins,
                           *specific_device_info);
}

#endif  // BUILDFLAG(IS_CHROMEOS)

namespace {

void ExpectDeviceObjectInfo(const base::DictValue& actual,
                            int vendor_id,
                            int product_id,
                            const std::string& name) {
  const std::optional<int> actual_vendor_id = actual.FindInt(kVendorIdKey);
  ASSERT_TRUE(actual_vendor_id);
  EXPECT_EQ(*actual_vendor_id, vendor_id);

  const std::optional<int> actual_product_id = actual.FindInt(kProductIdKey);
  ASSERT_TRUE(actual_product_id);
  EXPECT_EQ(*actual_product_id, product_id);

  const std::string* actual_device_name = actual.FindString(kDeviceNameKey);
  ASSERT_TRUE(actual_device_name);
  EXPECT_EQ(*actual_device_name, name);
}

void ExpectChooserObjectInfo(const UsbChooserContext::Object* actual,
                             const GURL& origin,
                             SettingSource source,
                             bool incognito,
                             int vendor_id,
                             int product_id,
                             const std::string& name) {
  ASSERT_TRUE(actual);
  EXPECT_EQ(actual->origin, origin);
  EXPECT_EQ(actual->source, source);
  EXPECT_EQ(actual->incognito, incognito);
  ExpectDeviceObjectInfo(actual->value, vendor_id, product_id, name);
}

}  // namespace

TEST_F(UsbChooserContextTest, GetGrantedObjectsWithOnlyPolicyAllowedDevices) {
  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  const auto kVendorOrigin = url::Origin::Create(GURL(kVendorUrl));
  auto objects = store->GetGrantedObjects(kVendorOrigin);
  ASSERT_EQ(objects.size(), 1u);

  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
}

TEST_F(UsbChooserContextTest,
       GetGrantedObjectsWithUserAndPolicyAllowedDevices) {
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  UsbDeviceInfoPtr persistent_device_info =
      device_manager_.CreateAndAddDevice(1000, 1, "Google", "Gizmo", "123ABC");
  UsbDeviceInfoPtr ephemeral_device_info =
      device_manager_.CreateAndAddDevice(1000, 2, "Google", "Gadget", "");

  auto* store = GetChooserContext(profile());

  const auto kVendorOrigin = url::Origin::Create(GURL(kVendorUrl));
  store->GrantDevicePermission(kVendorOrigin, *persistent_device_info);
  store->GrantDevicePermission(kVendorOrigin, *ephemeral_device_info);

  auto objects = store->GetGrantedObjects(kVendorOrigin);
  ASSERT_EQ(objects.size(), 3u);

  // The user granted permissions appear before the policy granted permissions.
  // Within these user granted permissions, the persistent device permissions
  // appear before ephemeral device permissions. The policy enforced objects
  // that are returned by GetGrantedObjects() are ordered by the tuple
  // (vendor_id, product_id) representing the device IDs.  Wildcard IDs are
  // represented by a value of -1, so they appear first.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kUser,
                          /*incognito=*/false,
                          /*vendor_id=*/1000,
                          /*product_id=*/1,
                          /*name=*/"Gizmo");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kUser,
                          /*incognito=*/false,
                          /*vendor_id=*/1000,
                          /*product_id=*/2,
                          /*name=*/"Gadget");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
}

TEST_F(UsbChooserContextTest,
       GetGrantedObjectsWithUserGrantedDeviceAllowedBySpecificDevicePolicy) {
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Google", "Gizmo", "123ABC");

  auto* store = GetChooserContext(profile());
  const auto kProductVendorOrigin =
      url::Origin::Create(GURL(kProductVendorUrl));
  store->GrantDevicePermission(kProductVendorOrigin, *persistent_device_info);

  auto objects = store->GetGrantedObjects(kProductVendorOrigin);
  ASSERT_EQ(objects.size(), 1u);

  // User granted permissions for a device that is also granted by a specific
  // device policy will be replaced by the policy permission. The object should
  // still retain the name of the device.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Gizmo");
}

TEST_F(UsbChooserContextTest,
       GetGrantedObjectsWithUserGrantedDeviceAllowedByVendorDevicePolicy) {
  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      6353, 1000, "Vendor", "Product", "123ABC");

  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  const auto kVendorOrigin = url::Origin::Create(GURL(kVendorUrl));
  store->GrantDevicePermission(kVendorOrigin, *persistent_device_info);

  auto objects = store->GetGrantedObjects(kVendorOrigin);
  ASSERT_EQ(objects.size(), 1u);

  // User granted permissions for a device that is also granted by a vendor
  // device policy will be replaced by the policy permission.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
}

TEST_F(UsbChooserContextTest,
       GetGrantedObjectsWithUserGrantedDeviceAllowedByAnyVendorDevicePolicy) {
  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      1123, 5813, "Some", "Product", "123ABC");

  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  const auto kAnyDeviceOrigin = url::Origin::Create(GURL(kAnyDeviceUrl));
  store->GrantDevicePermission(kAnyDeviceOrigin, *persistent_device_info);

  auto objects = store->GetGrantedObjects(kAnyDeviceOrigin);
  ASSERT_EQ(objects.size(), 1u);

  // User granted permissions for a device that is also granted by a wildcard
  // vendor policy will be replaced by the policy permission.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
}

TEST_F(UsbChooserContextTest,
       GetAllGrantedObjectsWithOnlyPolicyAllowedDevices) {
  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 4u);

  // The policy enforced objects that are returned by GetAllGrantedObjects() are
  // ordered by the tuple (vendor_id, product_id) representing the device IDs.
  // Wildcard IDs are represented by a value of -1, so they appear first.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Unknown product 0x162E from Google Inc.");
  ExpectChooserObjectInfo(objects[3].get(),
                          /*origin=*/GURL(kCoolUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6354,
                          /*product_id=*/1357,
                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
}

TEST_F(UsbChooserContextTest,
       GetAllGrantedObjectsWithUserAndPolicyAllowedDevices) {
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  const GURL kGoogleUrl("https://www.google.com");
  const auto kGoogleOrigin = url::Origin::Create(kGoogleUrl);
  UsbDeviceInfoPtr persistent_device_info =
      device_manager_.CreateAndAddDevice(1000, 1, "Google", "Gizmo", "123ABC");
  UsbDeviceInfoPtr ephemeral_device_info =
      device_manager_.CreateAndAddDevice(1000, 2, "Google", "Gadget", "");

  auto* store = GetChooserContext(profile());

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA))
      .Times(2);
  store->GrantDevicePermission(kGoogleOrigin, *persistent_device_info);
  store->GrantDevicePermission(kGoogleOrigin, *ephemeral_device_info);

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 6u);

  for (const auto& object : objects) {
    EXPECT_TRUE(store->IsValidObject(object->value));
  }

  // The user granted permissions appear before the policy granted permissions.
  // Within the user granted permissions, the persistent device permissions
  // are added to the vector before ephemeral device permissions.
  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/kGoogleUrl,
                          /*source=*/SettingSource::kUser,
                          /*incognito=*/false,
                          /*vendor_id=*/1000,
                          /*product_id=*/1,
                          /*name=*/"Gizmo");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/kGoogleUrl,
                          /*source=*/SettingSource::kUser,
                          /*incognito=*/false,
                          /*vendor_id=*/1000,
                          /*product_id=*/2,
                          /*name=*/"Gadget");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
  ExpectChooserObjectInfo(objects[3].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
  ExpectChooserObjectInfo(objects[4].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Unknown product 0x162E from Google Inc.");
  ExpectChooserObjectInfo(objects[5].get(),
                          /*origin=*/GURL(kCoolUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6354,
                          /*product_id=*/1357,
                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
}

TEST_F(UsbChooserContextTest,
       GetAllGrantedObjectsWithSpecificPolicyAndUserGrantedDevice) {
  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      6353, 5678, "Specific", "Product", "123ABC");

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  const auto kProductVendorOrigin =
      url::Origin::Create(GURL(kProductVendorUrl));
  store->GrantDevicePermission(kProductVendorOrigin, *persistent_device_info);

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 4u);

  for (const auto& object : objects) {
    EXPECT_TRUE(store->IsValidObject(object->value));
  }

  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Product");
  ExpectChooserObjectInfo(objects[3].get(),
                          /*origin=*/GURL(kCoolUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6354,
                          /*product_id=*/1357,
                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
  ASSERT_TRUE(persistent_device_info->product_name);
  EXPECT_EQ(store->GetObjectDisplayName(objects[2]->value),
            persistent_device_info->product_name.value());
}

TEST_F(UsbChooserContextTest,
       GetAllGrantedObjectsWithVendorPolicyAndUserGrantedDevice) {
  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      6353, 1000, "Vendor", "Product", "123ABC");

  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  const auto kVendorOrigin = url::Origin::Create(GURL(kVendorUrl));
  store->GrantDevicePermission(kVendorOrigin, *persistent_device_info);

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 4u);

  for (const auto& object : objects) {
    EXPECT_TRUE(store->IsValidObject(object->value));
  }

  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Unknown product 0x162E from Google Inc.");
  ExpectChooserObjectInfo(objects[3].get(),
                          /*origin=*/GURL(kCoolUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6354,
                          /*product_id=*/1357,
                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
}

TEST_F(UsbChooserContextTest,
       GetAllGrantedObjectsWithAnyPolicyAndUserGrantedDevice) {
  UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
      1123, 5813, "Some", "Product", "123ABC");

  auto* store = GetChooserContext(profile());
  profile()->GetPrefs()->SetList(prefs::kManagedWebUsbAllowDevicesForUrls,
                                 base::test::ParseJsonList(kPolicySetting));

  EXPECT_CALL(*mock_permission_observers_[profile()],
              OnObjectPermissionChanged(
                  std::make_optional(ContentSettingsType::USB_GUARD),
                  ContentSettingsType::USB_CHOOSER_DATA));
  const auto kAnyDeviceOrigin = url::Origin::Create(GURL(kAnyDeviceUrl));
  store->GrantDevicePermission(kAnyDeviceOrigin, *persistent_device_info);

  auto objects = store->GetAllGrantedObjects();
  ASSERT_EQ(objects.size(), 4u);

  for (const auto& object : objects) {
    EXPECT_TRUE(store->IsValidObject(object->value));
  }

  ExpectChooserObjectInfo(objects[0].get(),
                          /*origin=*/GURL(kAnyDeviceUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/kDeviceIdWildcard,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from any vendor");
  ExpectChooserObjectInfo(objects[1].get(),
                          /*origin=*/GURL(kVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/kDeviceIdWildcard,
                          /*name=*/"Devices from Google Inc.");
  ExpectChooserObjectInfo(objects[2].get(),
                          /*origin=*/GURL(kProductVendorUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6353,
                          /*product_id=*/5678,
                          /*name=*/"Unknown product 0x162E from Google Inc.");
  ExpectChooserObjectInfo(objects[3].get(),
                          /*origin=*/GURL(kCoolUrl),
                          /*source=*/SettingSource::kPolicy,
                          /*incognito=*/false,
                          /*vendor_id=*/6354,
                          /*product_id=*/1357,
                          /*name=*/"Unknown product 0x054D from vendor 0x18D2");
}

TEST_F(UsbChooserContextTest, MassStorageHidden) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);

  // Mass storage devices should be hidden.
  std::vector<device::mojom::UsbConfigurationInfoPtr> storage_configs;
  storage_configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0x08, 0x06, 0x50));
  UsbDeviceInfoPtr storage_device_info = device_manager_.CreateAndAddDevice(
      0, 0, "vendor1", "storage", "123ABC", std::move(storage_configs));

  // Composite devices with both mass storage and allowed interfaces should be
  // shown.
  std::vector<device::mojom::UsbConfigurationInfoPtr> complex_configs;
  complex_configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0x08, 0x06, 0x50, 1));
  complex_configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0xff, 0x42, 0x1, 2));
  UsbDeviceInfoPtr complex_device_info = device_manager_.CreateAndAddDevice(
      0, 0, "vendor2", "complex", "456DEF", std::move(complex_configs));

  UsbChooserContext* chooser_context = GetChooserContext(profile());

  base::RunLoop loop;
  chooser_context->GetDevices(
      base::BindLambdaForTesting([&](std::vector<UsbDeviceInfoPtr> devices) {
        EXPECT_EQ(1u, devices.size());
        EXPECT_EQ(complex_device_info->product_name, devices[0]->product_name);
        loop.Quit();
      }));
  loop.Run();
}

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(UsbChooserContextTest, MassStorageShownWhenDetachable) {
  base::ListValue allowlist;
  base::DictValue ids;
  ids.Set(ash::kUsbDetachableAllowlistKeyVid, 1234);
  ids.Set(ash::kUsbDetachableAllowlistKeyPid, 1);
  allowlist.Append(std::move(ids));

  profile()->ScopedCrosSettingsTestHelper()->GetStubbedProvider()->Set(
      ash::kUsbDetachableAllowlist, base::Value(std::move(allowlist)));

  GURL kUrl("https://www.google.com");
  const auto origin = url::Origin::Create(kUrl);

  // Mass storage devices should be hidden unless they are listed in the
  // UsbDetachableAllowlist policy.
  std::vector<device::mojom::UsbConfigurationInfoPtr> storage_configs;
  storage_configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0x08, 0x06, 0x50));
  UsbDeviceInfoPtr detachable_storage_device_info =
      device_manager_.CreateAndAddDevice(1234, 1, "vendor1",
                                         "detachable storage", "123ABC",
                                         std::move(storage_configs));

  storage_configs.clear();
  storage_configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0x08, 0x06, 0x50));
  UsbDeviceInfoPtr storage_device_info = device_manager_.CreateAndAddDevice(
      1234, 2, "vendor1", "storage", "456DEF", std::move(storage_configs));

  UsbChooserContext* chooser_context = GetChooserContext(profile());

  base::RunLoop loop;
  chooser_context->GetDevices(
      base::BindLambdaForTesting([&](std::vector<UsbDeviceInfoPtr> devices) {
        EXPECT_EQ(1u, devices.size());
        EXPECT_EQ(detachable_storage_device_info->product_name,
                  devices[0]->product_name);
        loop.Quit();
      }));
  loop.Run();
}
#endif  // BUILDFLAG(IS_CHROMEOS)

TEST_F(UsbChooserContextTest, DeviceWithNoInterfaceVisible) {
  GURL url("https://www.google.com");
  const auto origin = url::Origin::Create(url);

  std::vector<device::mojom::UsbConfigurationInfoPtr> configs;
  configs.push_back(
      device::FakeUsbDeviceInfo::CreateConfiguration(0x08, 0x06, 0x50));
  configs[0]->interfaces.clear();

  UsbDeviceInfoPtr device_info = device_manager_.CreateAndAddDevice(
      0, 0, "vendor1", "no_interface", "123ABC", std::move(configs));

  UsbChooserContext* chooser_context = GetChooserContext(profile());

  base::RunLoop loop;
  chooser_context->GetDevices(
      base::BindLambdaForTesting([&](std::vector<UsbDeviceInfoPtr> devices) {
        EXPECT_EQ(1u, devices.size());
        EXPECT_EQ(device_info->product_name, devices[0]->product_name);
        loop.Quit();
      }));
  loop.Run();
}
