// Copyright 2020 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/ui/extensions/settings_overridden_params_providers.h"

#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_url_overrides.h"
#include "chrome/browser/extensions/extension_url_overrides_registrar.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/search_test_utils.h"
#include "components/search_engines/template_url_service.h"
#include "extensions/browser/extension_registrar.h"
#include "extensions/browser/permissions/permissions_updater.h"
#include "extensions/browser/ui_util.h"
#include "extensions/common/extension_builder.h"

class SettingsOverriddenParamsProvidersUnitTest
    : public extensions::ExtensionServiceTestBase {
 public:
  void SetUp() override {
    extensions::ExtensionServiceTestBase::SetUp();
    InitializeEmptyExtensionService();

    // The NtpOverriddenDialogController rellies on ExtensionUrlOverrides;
    // ensure one exists.
    extensions::ExtensionUrlOverridesRegistrar::GetFactoryInstance()
        ->SetTestingFactoryAndUse(
            profile(),
            base::BindRepeating([](content::BrowserContext* context)
                                    -> std::unique_ptr<KeyedService> {
              return std::make_unique<
                  extensions::ExtensionUrlOverridesRegistrar>(context);
            }));
    auto* template_url_service = static_cast<TemplateURLService*>(
        TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
            profile(),
            base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor)));
    search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
  }

  // Adds a new extension that overrides the NTP.
  const extensions::Extension* AddExtensionControllingNewTab(
      const std::string& name = "ntp override") {
    base::DictValue chrome_url_overrides =
        base::DictValue().Set("newtab", "newtab.html");
    scoped_refptr<const extensions::Extension> extension =
        extensions::ExtensionBuilder(name)
            .SetLocation(extensions::mojom::ManifestLocation::kInternal)
            .SetManifestKey("chrome_url_overrides",
                            std::move(chrome_url_overrides))
            .Build();

    extensions::PermissionsUpdater(profile()).GrantActivePermissions(
        extension.get());
    registrar()->AddExtension(extension);
    EXPECT_EQ(extension, ExtensionUrlOverrides::GetExtensionControllingURL(
                             chrome::ChromeUINewTabURLAsGURL(), profile()));

    return extension.get();
  }
};

TEST_F(SettingsOverriddenParamsProvidersUnitTest,
       GetExtensionControllingNewTab) {
  // With no extensions installed, there should be no controlling extension.
  EXPECT_EQ(std::nullopt,
            settings_overridden_params::GetNtpOverriddenParams(profile()));

  // Install an extension, but not one that overrides the NTP. There should
  // still be no controlling extension.
  scoped_refptr<const extensions::Extension> regular_extension =
      extensions::ExtensionBuilder("regular").Build();
  registrar()->AddExtension(regular_extension);
  EXPECT_EQ(std::nullopt,
            settings_overridden_params::GetNtpOverriddenParams(profile()));

  // Finally, install an extension that overrides the NTP. It should be the
  // controlling extension.
  const extensions::Extension* ntp_extension = AddExtensionControllingNewTab();
  std::optional<ExtensionSettingsOverriddenDialog::Params> params =
      settings_overridden_params::GetNtpOverriddenParams(profile());
  ASSERT_TRUE(params);
  EXPECT_EQ(ntp_extension->id(), params->controlling_extension_id);

  // In this case, disabling the extension would go back to the default NTP, so
  // a specific message should show.
  EXPECT_EQ("Change back to Google?",
            base::UTF16ToUTF8(params->content.dialog_title));
}

// Tests that long extension names are truncated in the dialog message.
TEST_F(SettingsOverriddenParamsProvidersUnitTest,
       LongNameExtensionOverridingNewTab) {
  // With no extensions installed, there should be no controlling extension.
  EXPECT_EQ(std::nullopt,
            settings_overridden_params::GetNtpOverriddenParams(profile()));

  // Install an extensions which overrides the NTP and has a long name.
  const std::u16string extension_name =
      u"This extension name should be longer than our truncation threshold "
      "to test that the bubble can handle long names";
  const std::u16string truncated_name =
      extensions::ui_util::GetFixupExtensionNameForUIDisplay(extension_name);
  ASSERT_LT(truncated_name.size(), extension_name.size());

  const extensions::Extension* ntp_extension =
      AddExtensionControllingNewTab(base::UTF16ToUTF8(extension_name));
  std::optional<ExtensionSettingsOverriddenDialog::Params> params =
      settings_overridden_params::GetNtpOverriddenParams(profile());
  ASSERT_TRUE(params);
  ASSERT_EQ(ntp_extension->id(), params->controlling_extension_id);

  // The dialog message should contain the truncated name.
  EXPECT_TRUE(params->content.message.contains(truncated_name));
  EXPECT_FALSE(params->content.message.contains(extension_name));
}

TEST_F(SettingsOverriddenParamsProvidersUnitTest,
       DialogStringsWhenMultipleNtpOverrides_MultipleExtensions) {
  const extensions::Extension* extension1 =
      AddExtensionControllingNewTab("uno");
  const extensions::Extension* extension2 =
      AddExtensionControllingNewTab("dos");
  EXPECT_NE(extension1->id(), extension2->id());

  // When there are multiple extensions that could override the NTP, we should
  // show a generic dialog (rather than prompting to go back to the default
  // NTP), because the other extension would just take over.
  std::optional<ExtensionSettingsOverriddenDialog::Params> params =
      settings_overridden_params::GetNtpOverriddenParams(profile());
  ASSERT_TRUE(params);
  EXPECT_EQ(extension2->id(), params->controlling_extension_id);
  EXPECT_EQ("Did you mean to change this page?",
            base::UTF16ToUTF8(params->content.dialog_title));
}
