// Copyright 2023 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/views/extensions/extensions_menu_main_page_view.h"

#include "base/containers/to_vector.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/user_action_tester.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/extensions/chrome_test_extension_loader.h"
#include "chrome/browser/extensions/extension_action_runner.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_ui_util.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/toolbar/toolbar_action_view_model.h"
#include "chrome/browser/ui/views/controls/hover_button.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_coordinator.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_delegate_desktop.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_entry_view.h"
#include "chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.h"
#include "chrome/browser/ui/views/extensions/extensions_request_access_button.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_browsertest.h"
#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/host_access_request_helper.h"
#include "extensions/browser/permissions/active_tab_permission_granter.h"
#include "extensions/browser/permissions/site_permissions_helper.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/extension_features.h"
#include "extensions/test/permissions_manager_waiter.h"
#include "extensions/test/test_extension_dir.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/toggle_button.h"
#include "ui/views/view_utils.h"
#include "ui/views/views_switches.h"

namespace {

using PermissionsManager = extensions::PermissionsManager;
using SitePermissionsHelper = extensions::SitePermissionsHelper;

// Returns the extension names from the given `menu_entries`.
std::vector<std::string> GetExtensionNames(
    std::vector<ExtensionsMenuEntryView*> menu_entries) {
  return base::ToVector(menu_entries, [](ExtensionsMenuEntryView* entry) {
    return base::UTF16ToUTF8(entry->action_button_for_testing()->GetText());
  });
}

}  // namespace

class ExtensionsMenuMainPageViewBrowserTest
    : public ExtensionsToolbarBrowserTest {
 public:
  ExtensionsMenuMainPageViewBrowserTest();
  ~ExtensionsMenuMainPageViewBrowserTest() override = default;
  ExtensionsMenuMainPageViewBrowserTest(
      const ExtensionsMenuMainPageViewBrowserTest&) = delete;
  ExtensionsMenuMainPageViewBrowserTest& operator=(
      const ExtensionsMenuMainPageViewBrowserTest&) = delete;

  // Opens menu on "main page" by default.
  void ShowMenu();

  // Asserts there is exactly one menu entry and then returns it.
  ExtensionsMenuEntryView* GetOnlyMenuEntry();

  // Returns the extension names in the request access section. If it's empty,
  // the section is not visible.
  std::vector<extensions::ExtensionId> GetExtensionsInRequestsSection();

  // Returns the extension ids in the request access button in the toolbar.
  std::vector<extensions::ExtensionId> GetExtensionsInRequestAccessButton();

  // Since this is a unittest, the extensions menu widget sometimes needs a
  // nudge to re-layout the views.
  void LayoutMenuIfNecessary();

  void ClickSitePermissionsButton(ExtensionsMenuEntryView* menu_entry);

  // Clicks the site access toggle in the extension's menu main page. If
  // `active_tab_only` is false, it waits for user site access updated (toggling
  // an extension with just active tab grants tab permission, but doesn't change
  // the extension site access).
  void ClickSiteAccessToggle(ExtensionsMenuEntryView* menu_entry,
                             bool active_tab_only = false);

  ExtensionsMenuMainPageView* main_page();
  ExtensionsMenuSitePermissionsPageView* site_permissions_page();
  std::vector<ExtensionsMenuEntryView*> menu_entries();

  // Appends the embedded test server port to the expected site settings label.
  // In browser tests, embedded_test_server() binds to an ephemeral non-standard
  // port, which Chromium's URL display formatter includes in the displayed host
  // string (e.g. "example.com:<port>").
  std::u16string GetExpectedSiteSettingLabel(
      std::u16string_view base_label) const {
    return base::StrCat(
        {base_label, u":",
         base::NumberToString16(embedded_test_server()->port())});
  }

  // ExtensionsToolbarBrowserTest:
  void SetUpCommandLine(base::CommandLine* command_line) override;
  void TearDownOnMainThread() override;

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

ExtensionsMenuMainPageViewBrowserTest::ExtensionsMenuMainPageViewBrowserTest() {
  scoped_feature_list_.InitAndEnableFeature(
      extensions_features::kExtensionsMenuAccessControl);
}

void ExtensionsMenuMainPageViewBrowserTest::ShowMenu() {
  menu_coordinator()->Show(views::BubbleAnchor(extensions_button()),
                           extensions_container());
  if (views::Widget* menu_widget =
          menu_coordinator()->GetExtensionsMenuWidget()) {
    if (auto* bubble_delegate =
            menu_widget->widget_delegate()->AsBubbleDialogDelegate()) {
      bubble_delegate->set_close_on_deactivate(false);
    }
  }
}

ExtensionsMenuEntryView*
ExtensionsMenuMainPageViewBrowserTest::GetOnlyMenuEntry() {
  std::vector<ExtensionsMenuEntryView*> entries = menu_entries();
  if (entries.size() != 1u) {
    ADD_FAILURE() << "Not exactly one entry; size is: " << entries.size();
    return nullptr;
  }
  return *entries.begin();
}

std::vector<extensions::ExtensionId>
ExtensionsMenuMainPageViewBrowserTest::GetExtensionsInRequestsSection() {
  ExtensionsMenuMainPageView* page = main_page();
  return page ? page->GetExtensionsRequestingAccessForTesting()
              : std::vector<std::string>();
}

std::vector<extensions::ExtensionId>
ExtensionsMenuMainPageViewBrowserTest::GetExtensionsInRequestAccessButton() {
  return extensions_container()
      ->GetRequestAccessButton()
      ->GetExtensionIdsForTesting();
}

void ExtensionsMenuMainPageViewBrowserTest::LayoutMenuIfNecessary() {
  if (views::Widget* menu_widget =
          menu_coordinator()->GetExtensionsMenuWidget()) {
    menu_widget->LayoutRootViewIfNecessary();
  }
}

void ExtensionsMenuMainPageViewBrowserTest::ClickSitePermissionsButton(
    ExtensionsMenuEntryView* menu_entry) {
  ClickButton(menu_entry->site_permissions_button_for_testing());
  WaitForAnimation();
}

void ExtensionsMenuMainPageViewBrowserTest::ClickSiteAccessToggle(
    ExtensionsMenuEntryView* menu_entry,
    bool active_tab_only) {
  extensions::PermissionsManagerWaiter waiter(
      PermissionsManager::Get(profile()));
  ClickButton(menu_entry->site_access_toggle_for_testing());
  if (!active_tab_only) {
    waiter.WaitForExtensionPermissionsUpdate();
  }
  WaitForAnimation();
  LayoutMenuIfNecessary();
}

ExtensionsMenuMainPageView* ExtensionsMenuMainPageViewBrowserTest::main_page() {
  ExtensionsMenuDelegateDesktop* menu_delegate =
      menu_coordinator()->GetDelegateForTesting();
  return menu_delegate ? menu_delegate->GetMainPageViewForTesting() : nullptr;
}

ExtensionsMenuSitePermissionsPageView*
ExtensionsMenuMainPageViewBrowserTest::site_permissions_page() {
  ExtensionsMenuDelegateDesktop* menu_delegate =
      menu_coordinator()->GetDelegateForTesting();
  return menu_delegate ? menu_delegate->GetSitePermissionsPageForTesting()
                       : nullptr;
}

std::vector<ExtensionsMenuEntryView*>
ExtensionsMenuMainPageViewBrowserTest::menu_entries() {
  ExtensionsMenuMainPageView* page = main_page();
  return page ? page->GetMenuEntries()
              : std::vector<ExtensionsMenuEntryView*>();
}

void ExtensionsMenuMainPageViewBrowserTest::SetUpCommandLine(
    base::CommandLine* command_line) {
  ExtensionsToolbarBrowserTest::SetUpCommandLine(command_line);
  command_line->AppendSwitch(
      views::switches::kDisableInputEventActivationProtectionForTesting);
}

void ExtensionsMenuMainPageViewBrowserTest::TearDownOnMainThread() {
  if (views::Widget* menu_widget =
          menu_coordinator()->GetExtensionsMenuWidget()) {
    menu_widget->CloseNow();
  }
  ExtensionsToolbarBrowserTest::TearDownOnMainThread();
}

IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       ExtensionsAreSorted) {
  constexpr char kExtensionZName[] = "Z Extension";
  InstallExtension(kExtensionZName);
  constexpr char kExtensionAName[] = "A Extension";
  InstallExtension(kExtensionAName);
  constexpr char kExtensionBName[] = "b Extension";
  InstallExtension(kExtensionBName);
  constexpr char kExtensionCName[] = "C Extension";
  InstallExtension(kExtensionCName);

  ShowMenu();

  std::vector<ExtensionsMenuEntryView*> entries = menu_entries();
  ASSERT_EQ(entries.size(), 4u);

  // Basic std::sort would do A,C,Z,b however we want A,b,C,Z
  std::vector<std::string> expected_items{kExtensionAName, kExtensionBName,
                                          kExtensionCName, kExtensionZName};
  EXPECT_EQ(GetExtensionNames(entries), expected_items);
}

// Verifies the site access toggle and site permissions button properties for an
// extension that doesn't request site access.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       NoHostAccessRequested) {
  auto extension = InstallExtension("Extension");

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // When site setting is set to "customize by extension" (default):
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  //   - site permissions label is visible, enabled, and has the corresponding
  //     string ("No access needed").
  EXPECT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  auto* site_permissions_label =
      menu_entry->site_permissions_label_for_testing();
  EXPECT_TRUE(site_permissions_label->GetVisible());
  EXPECT_TRUE(site_permissions_label->GetEnabled());
  EXPECT_EQ(site_permissions_label->GetText(), u"No access needed");
  EXPECT_EQ(site_permissions_label->GetTooltipText(), std::u16string());
  EXPECT_EQ(site_permissions_label->GetViewAccessibility().GetCachedName(),
            u"No access needed");

  // When site setting is set to "block all extensions":
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
}

// Verifies the site access toggle and site permissions button properties for an
// enterprise extension that doesn't request host permissions.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       NoHostAccessRequested_EnterpriseExtension) {
  auto extension =
      InstallEnterpriseExtension("Extension", /*host_permissions*/ {});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // When site setting is set to "customize by extension" (default):
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  //   - site permissions label is visible, enabled, and has the corresponding
  //     string ("No access needed. Installed by your administrator").
  EXPECT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  auto* site_permissions_label =
      menu_entry->site_permissions_label_for_testing();
  EXPECT_TRUE(site_permissions_label->GetVisible());
  EXPECT_TRUE(site_permissions_label->GetEnabled());
  EXPECT_EQ(site_permissions_label->GetText(),
            u"No access needed. Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetTooltipText(),
            u"Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetViewAccessibility().GetCachedName(),
            u"No access needed. Installed by your administrator");

  // When site setting is set to "block all extensions":
  //   - site access toggle is hidden
  //   - site permissions button is hidden
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
}

// Verifies the site access toggle and site permissions button properties when
// toggling site access for an extension that requests host permissions for a
// specific site.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       HostPermissionsRequested_ToggleSiteAccess_OnSite) {
  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"*://*.example.com/*"});

  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // By default, site setting is set to "customize by extension" (default) and
  // extension has granted "on site" access:
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled and has "on site" text.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnSite);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(
      menu_entry->site_permissions_button_for_testing()->GetFocusBehavior(),
      views::View::FocusBehavior::ALWAYS);
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_SITE));

  // When site access is toggled OFF:
  //   - extension site access is "on click".
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled and has the corresponding
  //     strings.
  ClickSiteAccessToggle(menu_entry);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            u"Ask on every visit");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetTooltipText(),
            u"Change site permissions");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()
                ->GetViewAccessibility()
                .GetCachedName(),
            u"Ask on every visit. Select to change site permissions");

  // When site access is toggled ON:
  //   - extension site access is "on site", since that was the previous
  //     granted site access state.
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled and has the corresponding
  //     strings.
  ClickSiteAccessToggle(menu_entry);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnSite);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            u"Always on this site");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetTooltipText(),
            u"Change site permissions");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()
                ->GetViewAccessibility()
                .GetCachedName(),
            u"Always on this site. Select to change site permissions");
}

// Verifies the site access toggle and site permissions button properties when
// toggling site access for an extension that requests host permissions for all
// sites.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       HostPermissionsRequested_ToggleSiteAccess_OnAllSites) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // By default, site setting is set to "customize by extension" (default) and
  // extension has granted "on all sites" access:
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled and has the corresponding
  //     strings.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            u"Always on all sites");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetTooltipText(),
            u"Change site permissions");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()
                ->GetViewAccessibility()
                .GetCachedName(),
            u"Always on all sites. Select to change site permissions");

  // When site access is toggled OFF:
  //   - extension site access is changed to "on click".
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled and has the corresponding
  //     strings.
  ClickSiteAccessToggle(menu_entry);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            u"Ask on every visit");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetTooltipText(),
            u"Change site permissions");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()
                ->GetViewAccessibility()
                .GetCachedName(),
            u"Ask on every visit. Select to change site permissions");

  // When site access is toggled ON:
  //   - extension site access is "on all sites", since that was the previous
  //     granted site access state.
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled and has the corresponding
  //     strings.
  ClickSiteAccessToggle(menu_entry);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            u"Always on all sites");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetTooltipText(),
            u"Change site permissions");
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()
                ->GetViewAccessibility()
                .GetCachedName(),
            u"Always on all sites. Select to change site permissions");
}

// Verifies the site access toggle and site permissions button properties for an
// extension that requests host permissions when host permission change with the
// menu open.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       HostPermissionsRequested_DynamicUpdates) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // When site setting is set to "customize by extension" and extension site
  // permissions are granted "on all sites" (default):
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled, and has "on all sites"
  //   text.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(
      menu_entry->site_permissions_button_for_testing()->GetText(),
      l10n_util::GetStringUTF16(
          IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_ALL_SITES));

  // When site setting is set to "customize by extension" and extension site
  // permissions are withheld.
  //   - site access toggle is visible and off
  //   - site permissions button is visible, enabled and has "on click" text.
  WithholdHostPermissions(extension.get());
  LayoutMenuIfNecessary();
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When site setting is set to "block all extensions":
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
}

// Verifies the site access toggle persists its previous state when toggling
// site access on.
IN_PROC_BROWSER_TEST_F(
    ExtensionsMenuMainPageViewBrowserTest,
    HostPermissionsRequested_DynamicUpdates_TogglePersistsPreviousSiteAccess) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // By default, site setting is set to "customize by extension" (default) and
  // extension has granted "on all sites" access:
  //   - site access toggle is on.
  //   - site permissions button has "on all sites" text.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_EQ(
      menu_entry->site_permissions_button_for_testing()->GetText(),
      l10n_util::GetStringUTF16(
          IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_ALL_SITES));

  // Update site access to be "on click".
  //   - site access toggle is off
  //   - site permissions button has "on click" text.
  UpdateUserSiteAccess(*extension,
                       browser()->GetTabStripModel()->GetActiveWebContents(),
                       PermissionsManager::UserSiteAccess::kOnClick);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // Toggle extension site access ON:
  //   - site access toggle is on
  //   - site permissions button has "on all sites" text, since that was the
  //     previous granted site access state.
  ClickSiteAccessToggle(menu_entry);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_EQ(
      menu_entry->site_permissions_button_for_testing()->GetText(),
      l10n_util::GetStringUTF16(
          IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_ALL_SITES));
}

// Tests that opening the menu on a restricted URL correctly sets the site
// access toggle state to off.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       SiteAccessToggle_RestrictedUrl) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL restricted_url("chrome://extensions");
  NavigateAndCommit(restricted_url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // On restricted URLs, site access toggle is hidden and off.
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
}

// Tests that an extension with broad site access (kOnAllSites) whose site
// interaction evaluates to non-granted (e.g., on a policy-blocked host)
// correctly maintains toggle state matching UserSiteAccess (toggle is on).
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       SiteAccessToggle_BroadAccessWithWithheldInteraction) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  // Set up policy-blocked host pattern.
  URLPattern default_policy_blocked_pattern =
      URLPattern(URLPattern::SCHEME_ALL, "*://*.policy-blocked.com/*");
  extensions::URLPatternSet default_allowed_hosts;
  extensions::URLPatternSet default_blocked_hosts;
  default_blocked_hosts.AddPattern(default_policy_blocked_pattern);
  extensions::PermissionsData::SetDefaultPolicyHostRestrictions(
      extensions::util::GetBrowserContextId(browser()->GetProfile()),
      default_blocked_hosts, default_allowed_hosts);

  const GURL policy_blocked_url("https://www.policy-blocked.com");
  NavigateAndCommit(policy_blocked_url);

  // Extension has kOnAllSites access in PermissionsManager.
  ASSERT_EQ(GetUserSiteAccess(*extension, policy_blocked_url),
            PermissionsManager::UserSiteAccess::kOnAllSites);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // Verify that site_access_toggle is on (reflecting
  // UserSiteAccess::kOnAllSites) even though site interaction on this
  // policy-blocked URL evaluates to non-granted.
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
}

// Verifies the site access toggle and site permissions button properties for an
// extension that requests host permissions.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       HostPermissionsRequested_EnterpriseExtension) {
  auto extension =
      InstallEnterpriseExtension("Extension",
                                 /*host_permissions=*/{"<all_urls>"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // When site setting is set to "customize by extension" and has granted "on
  // all sites" access (default):
  //   - site access toggle is hidden, because extension has site access but
  //     user cannot withheld it.
  //   - site permissions button is hidden.
  //   - site permissions label is visible, enabled, and has the corresponding
  //     string ("Always on all sites. Installed by your administrator").
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  auto* site_permissions_label =
      menu_entry->site_permissions_label_for_testing();
  EXPECT_TRUE(site_permissions_label->GetVisible());
  EXPECT_TRUE(site_permissions_label->GetEnabled());
  EXPECT_EQ(site_permissions_label->GetText(),
            u"Always on all sites. Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetTooltipText(),
            u"Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetViewAccessibility().GetCachedName(),
            u"Always on all sites. Installed by your administrator");

  // When site setting is set to "block all extensions":
  //   - extension site access is still "on all sites".
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  //   - site permissions label is visible, enabled, and has the corresponding
  //     string ("Always on all sites. Installed by your administrator").
  // Note: Policy-installed extension can still run on the site even if the
  // user blocked all extensions because enterprise-installed extensions take
  // priority over user settings. Therefore, the button is visible (so the
  // user can see that it can run), but not clickable (because the user can't
  // modify the settings).
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnAllSites);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  site_permissions_label = menu_entry->site_permissions_label_for_testing();
  EXPECT_TRUE(site_permissions_label->GetVisible());
  EXPECT_TRUE(site_permissions_label->GetEnabled());
  EXPECT_EQ(site_permissions_label->GetText(),
            u"Always on all sites. Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetTooltipText(),
            u"Installed by your administrator");
  EXPECT_EQ(site_permissions_label->GetViewAccessibility().GetCachedName(),
            u"Always on all sites. Installed by your administrator");
}

// Verifies the site access toggle and site permissions button properties when
// toggling site access for an extension that only requests active tab.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       ActiveTabRequested_ToggleSiteAccess) {
  auto extension = InstallExtensionWithPermissions("Extension", {"activeTab"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // By default, site setting is set to "customize by extension" (default) and
  // extension has not active tab granted.
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled and has "on click" text.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  ASSERT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When site access toggle is toggled ON:
  //   - extension site access is "on click". Since extension only requested
  //     active tab, toggling site access on grants tab permissions but doesn't
  //     change the user site access.
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled and has "on click" text.
  ClickSiteAccessToggle(menu_entry, /*active_tab_only=*/true);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When site access toggle is toggled OFF:
  //   - extension site access is "on click".
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled and has "on click" text.
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  ClickSiteAccessToggle(menu_entry, /*active_tab_only=*/true);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));
}

// Verifies the site access toggle and site permissions button properties when
// toggling an extension off that has "on click" site access and granted tab
// permissions (meaning it has access to the current site).
IN_PROC_BROWSER_TEST_F(
    ExtensionsMenuMainPageViewBrowserTest,
    ActiveTabRequested_ToggleSiteAccess_UngrantTabPermissions) {
  auto extension = InstallExtensionWithPermissions("Extension", {"activeTab"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // By default, extension has not active tab granted. Thus, extension:
  //   - site interaction is "active tab".
  //   - site access is "on click".
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled, and has "on click" text.
  EXPECT_EQ(GetSiteInteraction(*extension, web_contents),
            SitePermissionsHelper::SiteInteraction::kActiveTab);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  auto reload_page_dialog_reset =
      extensions::ReloadPageDialogController::AcceptDialogForTesting(false);
  auto* action_runner =
      extensions::ExtensionActionRunner::GetForWebContents(web_contents);
  ASSERT_TRUE(action_runner);

  // When extension is granted tab permissions it has:
  //   - site interaction is "granted".
  //   - site access is "on click" since granting tab permissions doesn't change
  //   site access.
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled, and has "on click" text.
  {
    extensions::PermissionsManagerWaiter waiter(
        extensions::PermissionsManager::Get(browser()->GetProfile()));
    action_runner->GrantTabPermissions({extension.get()});
    waiter.WaitForActiveTabPermissionGranted(extension->id());
  }
  EXPECT_EQ(GetSiteInteraction(*extension, web_contents),
            SitePermissionsHelper::SiteInteraction::kGranted);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When extension is toggled OFF:
  //   - site interaction is "active tab".
  //   - site access is "on click".
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled, and has "on click" text.
  ClickSiteAccessToggle(menu_entry, /*active_tab_only=*/true);
  EXPECT_EQ(GetSiteInteraction(*extension, web_contents),
            SitePermissionsHelper::SiteInteraction::kActiveTab);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));
}

// Verifies the site access toggle and site permissions button properties for an
// extension that only requests active tab when site permissions change with the
// menu open.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       ActiveTabRequested_DynamicUpdates) {
  auto extension = InstallExtensionWithPermissions("Extension", {"activeTab"});

  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // When site setting is set to "customize by extension" (default) and active
  // tab is not granted:
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled, and has "on click" text.
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When site setting is set to "customize by extension" active tab is granted:
  //   - site access toggle is visible and on.
  //   - site permissions button is visible, enabled, and has "on click" text.
  //   Since extension only requested active tab, toggling site
  //     access on grants tab permissions but doesn't change the user site
  //     access.
  extensions::ActiveTabPermissionGranter* active_tab_permission_granter =
      extensions::ActiveTabPermissionGranter::FromWebContents(
          browser()->GetTabStripModel()->GetActiveWebContents());
  ASSERT_TRUE(active_tab_permission_granter);
  active_tab_permission_granter->GrantIfRequested(extension.get());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // Navigating to the same url after navigating to other url should remove tab
  // permissions. Therefore:
  //   - site access toggle is visible and off.
  //   - site permissions button is visible, enabled, and has "on click" text.
  // Note: refreshing the page doesn't revoke tab permissions, thus we
  // need to re navigate to the url.
  NavigateAndCommit(
      embedded_test_server()->GetURL("other-url.com", "/title1.html"));
  NavigateAndCommit(url);
  EXPECT_TRUE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetIsOn());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());
  EXPECT_EQ(menu_entry->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));

  // When site setting is set to "block all extensions":
  //   - site access toggle is hidden.
  //   - site permissions button is hidden.
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
}

// Verifies the site permissions button opens the site permissions page when it
// is enabled.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       SitePermissionsButton_OpenSitePermissionsPage) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  ShowMenu();
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();

  // Button is visible and enabled when site setting is set to "customize by
  // extension" (default setting).
  EXPECT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(menu_entry->site_permissions_button_for_testing()->GetEnabled());

  // Clicking on an extension's site permission enabled button should open
  // its site permission page in the menu.
  ClickSitePermissionsButton(menu_entry);
  EXPECT_FALSE(main_page());
  ExtensionsMenuSitePermissionsPageView* page = site_permissions_page();
  ASSERT_TRUE(page);
  EXPECT_EQ(page->extension_id(), extension->id());
}

IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       AddAndRemoveExtensionWhenMainPageIsOpen) {
  constexpr char kExtensionA[] = "A Extension";
  constexpr char kExtensionC[] = "C Extension";
  InstallExtension(kExtensionA);
  InstallExtension(kExtensionC);

  ShowMenu();

  // Verify the order of the extensions is A,C.
  {
    std::vector<ExtensionsMenuEntryView*> entries = menu_entries();
    ASSERT_EQ(entries.size(), 2u);
    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
    EXPECT_EQ(GetExtensionNames(entries), expected_names);
  }

  // Add a new extension while the menu is open.
  constexpr char kExtensionB[] = "B Extension";
  auto extensionB = InstallExtension(kExtensionB);
  LayoutMenuIfNecessary();

  // Extension should be added in the correct place.
  // Verify the new order is A,B,C.
  {
    std::vector<ExtensionsMenuEntryView*> entries = menu_entries();
    ASSERT_EQ(entries.size(), 3u);
    std::vector<std::string> expected_names{kExtensionA, kExtensionB,
                                            kExtensionC};
    EXPECT_EQ(GetExtensionNames(entries), expected_names);
  }

  // Remove a extension while the menu is open
  UninstallExtension(extensionB->id());
  LayoutMenuIfNecessary();

  // Verify the new order is A,C.
  {
    std::vector<ExtensionsMenuEntryView*> entries = menu_entries();
    ASSERT_EQ(entries.size(), 2u);
    std::vector<std::string> expected_names{kExtensionA, kExtensionC};
    EXPECT_EQ(GetExtensionNames(entries), expected_names);
  }
}

// Tests that removing the last extension while the main page is open closes the
// menu bubble.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       CloseMenuWhenLastExtensionIsRemoved) {
  auto extension = InstallExtension("Extension");

  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);
  EXPECT_TRUE(menu_coordinator()->IsShowing());

  UninstallExtension(extension->id());
  EXPECT_FALSE(menu_coordinator()->IsShowing());
}

// Tests that the extensions menu is dynamically updated when there is a
// navigation while the menu is opened.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       NavigationWhenMainPageIsOpen) {
  auto extension_A =
      InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"});
  auto extension_B = InstallExtensionWithHostPermissions(
      "Extension B", {"*://www.other.com/"});
  WithholdHostPermissions(extension_A.get());

  NavigateAndCommit(GURL("http://www.example.com"));

  // Add a site access request for extension A.
  AddHostAccessRequest(*extension_A,
                       browser()->GetTabStripModel()->GetActiveWebContents());

  ShowMenu();

  // Retrieve menu views for testing.
  ExtensionsMenuEntryView* extension_A_item = menu_entries()[0];
  ExtensionsMenuEntryView* extension_b_item = menu_entries()[1];
  ASSERT_EQ(extension_A_item->action_button_for_testing()->GetText(),
            u"Extension A");
  ASSERT_EQ(extension_b_item->action_button_for_testing()->GetText(),
            u"Extension B");
  const views::View* reload_section = main_page()->reload_section();
  const views::View* requests_section = main_page()->requests_section();

  // Verify site settings label has the current site, request access section
  // shows extension A request and extension items have the site access text
  // based on their access.
  ASSERT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            GetExpectedSiteSettingLabel(u"Allow extensions on example.com"));
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id()));
  EXPECT_EQ(extension_A_item->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));
  EXPECT_FALSE(
      extension_b_item->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(
      extension_b_item->site_permissions_label_for_testing()->GetVisible());
  EXPECT_EQ(extension_b_item->site_permissions_label_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_NONE));

  // Navigate to a same-origin site.
  NavigateAndCommit(GURL("http://www.example.com/title2.html"));
  LayoutMenuIfNecessary();

  // Verify site settings label has the new site, request access section still
  // shows the extension A request and extension items have the same site access
  // text.
  ASSERT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            GetExpectedSiteSettingLabel(u"Allow extensions on example.com"));
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id()));
  EXPECT_EQ(extension_A_item->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));
  EXPECT_FALSE(
      extension_b_item->site_permissions_button_for_testing()->GetVisible());
  EXPECT_TRUE(
      extension_b_item->site_permissions_label_for_testing()->GetVisible());
  EXPECT_EQ(extension_b_item->site_permissions_label_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_NONE));

  // Navigate to a cross-origin site.
  NavigateAndCommit(GURL("http://www.other.com"));
  LayoutMenuIfNecessary();

  // Verify site settings label has the new site, request access section is not
  // visible (requests are reset on cross-origin navigations) and extension
  // items updated their site access text based on their access.
  ASSERT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            GetExpectedSiteSettingLabel(u"Allow extensions on other.com"));
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());
  EXPECT_EQ(extension_A_item->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_CLICK));
  EXPECT_EQ(extension_b_item->site_permissions_button_for_testing()->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_SITE));
}

// Verifies the pin button appears on the menu item, in place of context menu
// button when state is normal, when an extension is pinned.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       PinnedExtensions) {
  auto extension = InstallExtension("Test Extension");

  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);
  HoverButton* context_menu_button =
      GetOnlyMenuEntry()->context_menu_button_for_testing();

  const ui::ColorProvider* color_provider =
      context_menu_button->GetColorProvider();
  auto three_dot_icon = gfx::Image(gfx::CreateVectorIcon(
      features::IsRoundedIconsEnabled() ? kMoreVertIcon
                                        : kBrowserToolsChromeRefreshOldIcon,
      color_provider->GetColor(kColorExtensionMenuIcon)));

  // Verify context menu button has three dot icon for all button states.
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_NORMAL)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_HOVERED)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_PRESSED)),
      three_dot_icon));

  // Pin extension.
  auto* toolbar_model = ToolbarActionsModel::Get(profile());
  ASSERT_TRUE(toolbar_model);
  toolbar_model->SetActionVisibility(extension->id(), true);
  LayoutMenuIfNecessary();

  // Verify context menu button changes to pin icon only in normal state.
  // Pin icon is tested by checking it is NOT the three dot icon because of a
  // weird reason when retrieving the pin icon. We can do this because a) no
  // other icon is expected, and b) pin icon correctly appears when manually
  // testing.
  EXPECT_FALSE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_NORMAL)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_HOVERED)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_PRESSED)),
      three_dot_icon));

  // Unpin extension.
  toolbar_model->SetActionVisibility(extension->id(), false);
  LayoutMenuIfNecessary();

  // Verify context menu button has three dot icon for all button states.
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_NORMAL)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_HOVERED)),
      three_dot_icon));
  EXPECT_TRUE(gfx::test::AreImagesEqual(
      gfx::Image(context_menu_button->GetImage(views::Button::STATE_PRESSED)),
      three_dot_icon));
}

IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       DisableAndEnableExtension) {
  constexpr char kName[] = "Test Extension";
  auto extension_id = InstallExtension(kName)->id();

  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);

  DisableExtension(extension_id);
  LayoutMenuIfNecessary();

  EXPECT_EQ(menu_entries().size(), 0u);

  EnableExtension(extension_id);
  LayoutMenuIfNecessary();

  EXPECT_EQ(menu_entries().size(), 1u);
}

// Tests that when an extension is reloaded it remains visible in the extensions
// menu.
// TODO(crbug.com/40879945): Verify context menu button shows the correct icon
// as pinned state is also preserved when a reload happens. Add this
// functionality when showing pin icon instead of context menu when extension is
// pinned is added.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest, ReloadExtension) {
  // The extension must have a manifest to be reloaded.
  extensions::TestExtensionDir extension_directory;
  constexpr char kManifest[] = R"({
        "name": "Test Extension",
        "version": "1",
        "manifest_version": 3
      })";
  extension_directory.WriteManifest(kManifest);
  extensions::ChromeTestExtensionLoader loader(profile());
  scoped_refptr<const extensions::Extension> extension =
      loader.LoadExtension(extension_directory.UnpackedPath());

  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);

  // Reload the extension.
  extensions::TestExtensionRegistryObserver registry_observer(
      extensions::ExtensionRegistry::Get(profile()));
  ReloadExtension(extension->id());
  ASSERT_TRUE(registry_observer.WaitForExtensionLoaded());
  LayoutMenuIfNecessary();

  // Re-open the menu (because it was closed when the only extension was
  // unloaded) to show the reloaded extension.
  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);
}

// Tests that a when an extension is reloaded with manifest errors, and
// therefore fails to be loaded into Chrome, it's removed from the
// extensions menu.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       ReloadExtensionFailed) {
  extensions::TestExtensionDir extension_directory;
  constexpr char kManifest[] = R"({
        "name": "Test Extension",
        "version": "1",
        "manifest_version": 3
      })";
  extension_directory.WriteManifest(kManifest);
  extensions::ChromeTestExtensionLoader loader(profile());
  scoped_refptr<const extensions::Extension> extension =
      loader.LoadExtension(extension_directory.UnpackedPath());

  ShowMenu();
  EXPECT_EQ(menu_entries().size(), 1u);

  // Replace the extension's valid manifest with one containing errors. In this
  // case, 'version' keys is missing.
  constexpr char kManifestWithErrors[] = R"({
        "name": "Test",
        "manifest_version": 3,
      })";
  extension_directory.WriteManifest(kManifestWithErrors);

  // Reload the extension. It should fail due to the manifest errors.
  extension_registrar()->ReloadExtensionWithQuietFailure(extension->id());
  base::RunLoop().RunUntilIdle();
  LayoutMenuIfNecessary();

  // Verify the menu is no longer showing because the only extension was
  // removed.
  EXPECT_FALSE(menu_coordinator()->IsShowing());
}

// Test that user controls in the menu are hidden on restricted sites, since
// user cannot change extension's site settings for them.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest, RestrictedSite) {
  constexpr char kExtension[] = "Extension";
  constexpr char kEnterpriseExtension[] = "Enterprise extension";
  InstallExtension(kExtension);
  InstallEnterpriseExtension(kEnterpriseExtension,
                             /*host_permissions=*/{"<all_urls>"});

  const GURL restricted_url("chrome://extensions");
  auto restricted_origin = url::Origin::Create(restricted_url);
  NavigateAndCommit(restricted_url);

  ShowMenu();
  ASSERT_EQ(menu_entries().size(), 2u);

  // Verify site settings tooltip and toggle are hidden, since no extension can
  // customize a restricted site.
  EXPECT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            u"Extensions are not allowed on chrome://extensions");
  EXPECT_FALSE(main_page()->site_settings_tooltip()->GetVisible());
  EXPECT_FALSE(main_page()->GetSiteSettingsToggleForTesting()->GetVisible());

  // Verify reload section and requests section are hidden.
  EXPECT_FALSE(main_page()->reload_section()->GetVisible());
  EXPECT_FALSE(main_page()->requests_section()->GetVisible());

  // Verify both extensions':
  //   - site access toggle is hidden, since site access cannot be changed
  //   - site permission button is hidden, since restricted sites have priority
  //   over enterprise extensions.
  EXPECT_FALSE(
      menu_entries()[0]->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(
      menu_entries()[1]->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(
      menu_entries()[0]->site_permissions_button_for_testing()->GetVisible());
  EXPECT_FALSE(
      menu_entries()[1]->site_permissions_button_for_testing()->GetVisible());
}

// Test that user controls in the menu are hidden or disabled on policy blocked
// sites, since user cannot change extension's site settings for them.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       PolicyBlockedSite) {
  // Install extensions requesting site access.
  InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  InstallExtensionWithPermissions("Extension: activeTab", {"activeTab"});

  // Add a policy-blocked site.
  URLPattern default_policy_blocked_pattern =
      URLPattern(URLPattern::SCHEME_ALL, "*://*.policy-blocked.com/*");
  extensions::URLPatternSet default_allowed_hosts;
  extensions::URLPatternSet default_blocked_hosts;
  default_blocked_hosts.AddPattern(default_policy_blocked_pattern);
  extensions::PermissionsData::SetDefaultPolicyHostRestrictions(
      extensions::util::GetBrowserContextId(browser()->GetProfile()),
      default_blocked_hosts, default_allowed_hosts);

  // Navigate to the policy-blocked site.
  const GURL policy_blocked_url("https://www.policy-blocked.com");
  auto restricted_origin = url::Origin::Create(policy_blocked_url);
  NavigateAndCommit(policy_blocked_url);

  ShowMenu();
  ASSERT_EQ(menu_entries().size(), 2u);

  // Verify site settings':
  //    - label says extensions are not allowed
  //    - tooltip is hidden since there is no enterprise extension that has
  //      site access.
  //    - toggle is hidden since user cannot customize any extension on a
  //    policy-blocked-site.
  EXPECT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            GetExpectedSiteSettingLabel(
                u"Extensions are not allowed on policy-blocked.com"));
  EXPECT_FALSE(main_page()->site_settings_tooltip()->GetVisible());
  EXPECT_FALSE(main_page()->GetSiteSettingsToggleForTesting()->GetVisible());

  // Verify reload section and requests section are hidden.
  EXPECT_FALSE(main_page()->reload_section()->GetVisible());
  EXPECT_FALSE(main_page()->requests_section()->GetVisible());

  // Retrieve menu entries.
  ExtensionsMenuEntryView* extension_item = menu_entries()[0];
  ExtensionsMenuEntryView* activeTab_extension_item = menu_entries()[1];
  ASSERT_EQ(extension_item->action_button_for_testing()->GetText(),
            u"Extension");
  ASSERT_EQ(activeTab_extension_item->action_button_for_testing()->GetText(),
            u"Extension: activeTab");

  // Verify all extensions':
  //   - site access toggle is hidden, since site access cannot be changed.
  //   - site permissions button is hidden.
  //   - site permissions label is visible and has "none" access. We leave them
  //     visible because enterprise extensions can still have access to the
  //     site.
  //     TODO(crbug.com/40879945): Consider only showing the site permissions
  //     button only for enterprise installed extensions on policy-blocked
  //     sites, similar to how we do for user-blocked sites.
  EXPECT_FALSE(extension_item->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(
      activeTab_extension_item->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(
      extension_item->site_permissions_button_for_testing()->GetVisible());
  EXPECT_FALSE(activeTab_extension_item->site_permissions_button_for_testing()
                   ->GetVisible());
  auto* extension_label = extension_item->site_permissions_label_for_testing();
  auto* activeTab_extension_label =
      activeTab_extension_item->site_permissions_label_for_testing();
  EXPECT_TRUE(extension_label->GetVisible());
  EXPECT_TRUE(activeTab_extension_label->GetVisible());
  EXPECT_EQ(extension_label->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_NONE));
  EXPECT_EQ(activeTab_extension_label->GetText(),
            l10n_util::GetStringUTF16(
                IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_NONE));
}

// Test that user controls in the menu are hidden or disabled on policy blocked
// sites, since user cannot change extension's site settings for them, and
// informs the user that enterprise extensions still have access.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       PolicyBlockedSite_EnterpriseExtension) {
  // Add a policy-blocked site.
  URLPattern default_policy_blocked_pattern =
      URLPattern(URLPattern::SCHEME_ALL, "*://*.policy-blocked.com/*");
  extensions::URLPatternSet default_allowed_hosts;
  extensions::URLPatternSet default_blocked_hosts;
  default_blocked_hosts.AddPattern(default_policy_blocked_pattern);
  extensions::PermissionsData::SetDefaultPolicyHostRestrictions(
      extensions::util::GetBrowserContextId(browser()->GetProfile()),
      default_blocked_hosts, default_allowed_hosts);

  // Install extensions requesting host permissions or activeTab.
  auto enterprise_extension =
      InstallEnterpriseExtension("Extension: enterprise",
                                 /*host_permissions=*/{"<all_urls>"});

  // Allow enterprise extension access to policy-blocked site.
  extensions::URLPatternSet allowed_hosts;
  extensions::URLPatternSet blocked_hosts;
  allowed_hosts.AddPattern(default_policy_blocked_pattern);
  enterprise_extension->permissions_data()->SetPolicyHostRestrictions(
      blocked_hosts, allowed_hosts);

  // Navigate to the policy-blocked site.
  const GURL policy_blocked_url("https://www.policy-blocked.com");
  auto restricted_origin = url::Origin::Create(policy_blocked_url);
  NavigateAndCommit(policy_blocked_url);

  ShowMenu();
  ASSERT_EQ(menu_entries().size(), 1u);

  // Verify site settings':
  //    - label says extensions are not allowed
  //    - tooltip is visible since an enterprise extension still has access
  //    - toggle is hidden since user cannot customize any extension on a
  //      policy-blocked-site.
  EXPECT_EQ(main_page()->GetSiteSettingLabelForTesting(),
            GetExpectedSiteSettingLabel(
                u"Extensions are not allowed on policy-blocked.com"));
  EXPECT_TRUE(main_page()->site_settings_tooltip());
  EXPECT_FALSE(main_page()->GetSiteSettingsToggleForTesting()->GetVisible());

  // Verify reload section and requests section are hidden.
  EXPECT_FALSE(main_page()->reload_section()->GetVisible());
  EXPECT_FALSE(main_page()->requests_section()->GetVisible());

  // Retrieve menu item.
  ExtensionsMenuEntryView* enterprise_extension_item = menu_entries()[0];
  ASSERT_EQ(enterprise_extension_item->action_button_for_testing()->GetText(),
            u"Extension: enterprise");

  // Verify extension's:
  //   - site access toggle is hidden, since site access cannot be changed
  //   - site permissions button is hidden.
  //   - site permissions label is visible, enabled, and "on all sites" since
  //     enterprise extension can have access to a policy-blocked site.
  EXPECT_FALSE(enterprise_extension_item->site_access_toggle_for_testing()
                   ->GetVisible());
  EXPECT_FALSE(enterprise_extension_item->site_permissions_button_for_testing()
                   ->GetVisible());
  auto* enterprise_label =
      enterprise_extension_item->site_permissions_label_for_testing();
  EXPECT_TRUE(enterprise_label->GetVisible());
  EXPECT_TRUE(enterprise_label->GetEnabled());
  EXPECT_EQ(
      enterprise_label->GetText(),
      l10n_util::GetStringUTF16(
          IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_ALL_SITES) +
          u". Installed by your administrator");
}

// Test that user controls for extensions in the menu are hidden on user
// blocked sites, since user cannot change the individual extension's site
// access.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest, UserBlockedSite) {
  InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});

  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  NavigateAndCommit(url);

  // Block all extensions on `url`.
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kBlockAllExtensions);

  ShowMenu();

  // Site setting tooltip is hidden and toggle is off.
  EXPECT_FALSE(main_page()->site_settings_tooltip()->GetVisible());
  EXPECT_FALSE(main_page()->GetSiteSettingsToggleForTesting()->GetIsOn());

  // Reload and requests section are hidden.
  EXPECT_FALSE(main_page()->reload_section()->GetVisible());
  EXPECT_FALSE(main_page()->requests_section()->GetVisible());

  // Menu item controls are hidden since extensions are blocked on this site.
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
}

// Test that user controls for enterprise extensions in the menu are disabled on
// user blocked sites, since user cannot change the individual extension's site
// access.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       UserBlockedSite_EnterpriseExtension) {
  InstallEnterpriseExtension("Enterprise extension", {"<all_urls>"});

  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  NavigateAndCommit(url);

  // Block all extensions on `url`.
  UpdateUserSiteSetting(
      PermissionsManager::UserSiteSetting::kBlockAllExtensions, url);
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kBlockAllExtensions);

  ShowMenu();

  // Site setting tooltip is visible, since the enterprise extension still has
  // access, and toggle is off.
  EXPECT_TRUE(main_page()->site_settings_tooltip()->GetVisible());
  EXPECT_FALSE(main_page()->GetSiteSettingsToggleForTesting()->GetIsOn());

  // Reload and requests section are hidden.
  EXPECT_FALSE(main_page()->reload_section()->GetVisible());
  EXPECT_FALSE(main_page()->requests_section()->GetVisible());

  // Site access toggle is hidden since user cannot customize the extension's
  // access. However, the site permissions button is hidden, and the site
  // permissions label is visible since the enterprise extension still has
  // access to the site.
  ExtensionsMenuEntryView* menu_entry = GetOnlyMenuEntry();
  EXPECT_FALSE(menu_entry->site_access_toggle_for_testing()->GetVisible());
  EXPECT_FALSE(menu_entry->site_permissions_button_for_testing()->GetVisible());
  auto* site_permissions_label =
      menu_entry->site_permissions_label_for_testing();
  EXPECT_TRUE(site_permissions_label->GetVisible());
  EXPECT_TRUE(site_permissions_label->GetEnabled());
  EXPECT_EQ(
      site_permissions_label->GetText(),
      l10n_util::GetStringUTF16(
          IDS_EXTENSIONS_MENU_MAIN_PAGE_EXTENSION_SITE_ACCESS_ON_ALL_SITES) +
          u". Installed by your administrator");
}

// Tests that the requests section is visible when the user can customize the
// extensions site access and at least 1+ extensions added a site access
// request. Reload section is always hidden if requests section is visible.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest, RequestsSection) {
  // Install two extension that requests host permissions.
  auto extension_A =
      InstallExtensionWithHostPermissions("Extension A", {"<all_urls>"});
  auto extension_B =
      InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"});

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  // By default, user can customize the site access of each extension and site
  // access will be granted
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);

  ShowMenu();
  const views::View* reload_section = main_page()->reload_section();
  const views::View* requests_section = main_page()->requests_section();

  // Requests section is hidden when user can customize site access but all
  // extensions have granted access.
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());

  // Requests section is hidden when extensions with withheld access haven't
  // added a site access request.
  WithholdHostPermissions(extension_A.get());
  WithholdHostPermissions(extension_B.get());
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());

  // Requests section is visible and has extension A when extension A added a
  // site access request.
  AddHostAccessRequest(*extension_A, web_contents);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id()));

  // Requests section is visible and has extension A and B when both extensions
  // added site access requests.
  AddHostAccessRequest(*extension_B, web_contents);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id(), extension_B->id()));

  // Message section is visible and has extension B when extension A removed its
  // site access request.
  RemoveHostAccessRequest(*extension_A, web_contents);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_B->id()));

  // Requests section is hidden when extension B removed its site access request
  // and no other extension has an active site access request.
  RemoveHostAccessRequest(*extension_B, web_contents);
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());
}

// Tests that the requests section is visible when the user can customize the
// extensions site access and an extension added a site access request with a
// pattern filter that matches the current site.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       RequestsSection_RequestsWithPattern) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  WithholdHostPermissions(extension.get());

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  // By default, user can customize the site access of each extension and site
  // access will be granted
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);

  ShowMenu();
  const views::View* reload_section = main_page()->reload_section();
  const views::View* requests_section = main_page()->requests_section();

  // Requests section is hidden when extension with withheld access hasn't added
  // a site access request.
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());

  // Requests section is hidden when extension adds a site access request with
  // filter that doesn't match the current web contents.
  URLPattern filter(extensions::Extension::kValidHostPermissionSchemes,
                    "http://www.other.com/*");
  AddHostAccessRequest(*extension, web_contents, filter);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());

  // Requests section is visible and has extension when the extension added a
  // site access request with filter that matches the current web contents.
  filter = URLPattern(extensions::Extension::kValidHostPermissionSchemes,
                      "http://www.example.com/*");
  AddHostAccessRequest(*extension, web_contents, filter);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));

  // Requests section is hidden when the extension added a site access request
  // with filter that doesn't match the current web contents (previous request
  // was removed).
  filter = URLPattern(extensions::Extension::kValidHostPermissionSchemes,
                      "http://www.example.com/other*");
  AddHostAccessRequest(*extension, web_contents, filter);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());
}

// Tests that the requests section is visible when the user can customize the
// extensions site access and an extension added a site access request with a
// pattern filter that matches the current site after same-origin navigations.
IN_PROC_BROWSER_TEST_F(
    ExtensionsMenuMainPageViewBrowserTest,
    RequestsSection_RequestsWithPattern_NavigationBetweenPages) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  WithholdHostPermissions(extension.get());

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);

  // By default, user can customize the site access of each extension and site
  // access will be granted
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);

  ShowMenu();
  const views::View* reload_section = main_page()->reload_section();
  const views::View* requests_section = main_page()->requests_section();

  // Requests section is hidden when extension adds a site access request for
  // extension with a filter that doesn't match the current web contents.
  URLPattern filter(extensions::Extension::kValidHostPermissionSchemes,
                    "*://*/title2.html*");
  AddHostAccessRequest(*extension,
                       browser()->GetTabStripModel()->GetActiveWebContents(),
                       filter);
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());

  // Navigate to a same-origin site that matches the filter.
  // Requests section is visible and has extension when the extension added a
  // site access request with filter that matches the current web contents.
  NavigateAndCommit(GURL("http://www.example.com/title2.html"));
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));

  // Navigate to a cross-origin site that matches the filters. Since it's a
  // cross-origin navigation, requests are reset.
  // Requests section is hidden when no extension has site access requests for
  // the current site.
  NavigateAndCommit(GURL("http://www.other.com/"));
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_FALSE(requests_section->GetVisible());
}

// Tests that the extension's request entry is removed from the requests
// container when an extension is granted site access.
IN_PROC_BROWSER_TEST_F(
    ExtensionsMenuMainPageViewBrowserTest,
    MessageSection_UserCustomizedAccess_ExtensionGrantedSiteAccess) {
  // Install two extension that requests host permissions.
  auto extension_A = InstallExtensionWithHostPermissions(
      "Extension A", {"*://www.example.com/*"});
  auto extension_B =
      InstallExtensionWithHostPermissions("Extension B", {"<all_urls>"});
  WithholdHostPermissions(extension_A.get());
  WithholdHostPermissions(extension_B.get());

  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  ShowMenu();
  const views::View* requests_section = main_page()->requests_section();

  // Add site access requests for both extensions and verify they are both
  // visible on the requests section.
  AddHostAccessRequest(*extension_A, web_contents);
  AddHostAccessRequest(*extension_B, web_contents);
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id(), extension_B->id()));

  // Grant site access to extension B and verify requests section only has
  // extension A, since extension's B request was removed when the extension
  // gained access to the site.
  UpdateUserSiteAccess(*extension_B, web_contents,
                       PermissionsManager::UserSiteAccess::kOnSite);
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension_A->id()));
}

// Tests that clicking on the extension's request 'allow' button grants site
// access one time.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       MessageSection_UserCustomizedAccess_AllowExtension) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  WithholdHostPermissions(extension.get());

  // Navigate to a site and add a site access request for the extension.
  const GURL url =
      embedded_test_server()->GetURL("example.com", "/title1.html");
  NavigateAndCommit(url);
  AddHostAccessRequest(*extension,
                       browser()->GetTabStripModel()->GetActiveWebContents());

  ShowMenu();
  const views::View* requests_section = main_page()->requests_section();

  constexpr char kActivatedUserAction[] =
      "Extensions.Toolbar.ExtensionActivatedFromAllowingRequestAccessInMenu";
  base::UserActionTester user_action_tester;

  // When extension added a site access request:
  //   - requests section (menu) includes extension and is visible.
  //   - request access button (toolbar) includes extension.
  //   - action has not been run.
  //   - site access is "on click".
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));
  EXPECT_THAT(GetExtensionsInRequestAccessButton(),
              testing::ElementsAre(extension->id()));
  EXPECT_EQ(user_action_tester.GetActionCount(kActivatedUserAction), 0);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnClick);

  // Click on the allow button for the extension.
  views::View* request_entry =
      main_page()->GetExtensionRequestingAccessEntryForTesting(extension->id());
  ASSERT_TRUE(request_entry);
  views::Button* request_allow_button =
      static_cast<views::Button*>(request_entry->children()[3]);
  extensions::PermissionsManagerWaiter waiter(
      PermissionsManager::Get(profile()));
  ClickButton(request_allow_button);
  waiter.WaitForExtensionPermissionsUpdate();

  WaitForAnimation();
  LayoutContainerIfNecessary();
  LayoutMenuIfNecessary();

  // When extension is granted site access via 'allow' button:
  //   - requests section (menu) does not include extension and is hidden
  //   - request access button (toolbar) does not include extension
  //   - action has been run
  //   - site access is "on site" since clicking the button grants always access
  //     to the site.
  EXPECT_FALSE(requests_section->GetVisible());
  EXPECT_TRUE(GetExtensionsInRequestsSection().empty());
  EXPECT_TRUE(GetExtensionsInRequestAccessButton().empty());
  EXPECT_EQ(user_action_tester.GetActionCount(kActivatedUserAction), 1);
  EXPECT_EQ(GetUserSiteAccess(*extension, url),
            PermissionsManager::UserSiteAccess::kOnSite);
}

// Tests that clicking on the extension's request 'dismiss' button removes the
// extension request from the menu and toolbar.
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       MessageSection_UserCustomizedAccess_DismissExtension) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  WithholdHostPermissions(extension.get());

  // Navigate to a site and add a site access request for the extension.
  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  AddHostAccessRequest(*extension,
                       browser()->GetTabStripModel()->GetActiveWebContents());

  ShowMenu();
  const views::View* requests_section = main_page()->requests_section();

  // When extension added a site access request:
  //   - requests section (menu) includes extension and is visible.
  //   - request access button (toolbar) includes extension.
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));
  EXPECT_THAT(GetExtensionsInRequestAccessButton(),
              testing::ElementsAre(extension->id()));

  // Click on the dismiss button for the extension.
  views::View* request_entry =
      main_page()->GetExtensionRequestingAccessEntryForTesting(extension->id());
  ASSERT_TRUE(request_entry);
  ASSERT_EQ(request_entry->children().size(), 4u);
  views::Button* request_dismiss_button =
      static_cast<views::Button*>(request_entry->children()[2]);
  ClickButton(request_dismiss_button);

  WaitForAnimation();
  LayoutContainerIfNecessary();
  LayoutMenuIfNecessary();

  // When the extension requests are dismissed:
  //   - requests section (menu) does not include extension and is hidden
  //   - request access button (toolbar) does not include extension
  EXPECT_FALSE(requests_section->GetVisible());
  EXPECT_TRUE(GetExtensionsInRequestsSection().empty());
  EXPECT_TRUE(GetExtensionsInRequestAccessButton().empty());

  // Re navigate to the same page.
  // Note: refreshing the page doesn't revoke tab permissions, thus we
  // need to re navigate to the url.
  NavigateAndCommit(GURL("http://other-url.com"));
  NavigateAndCommit(url);

  // Navigating to the same url should not show again the request, since
  // requests are reset on cross-origin navigation.
  //   - message section (menu) does not include extension and is hidden
  //   - request access button (toolbar) does not include extension.
  EXPECT_FALSE(requests_section->GetVisible());
  EXPECT_TRUE(GetExtensionsInRequestsSection().empty());
  EXPECT_TRUE(GetExtensionsInRequestAccessButton().empty());
}

// Tests that the requests section displays an extension with a site access
// request even if it is not allowed to show requests on the toolbar (extensions
// menu is not considered part of the toolbar for this).
IN_PROC_BROWSER_TEST_F(ExtensionsMenuMainPageViewBrowserTest,
                       RequestsSection_RequestNotAllowedOnToolbar) {
  auto extension =
      InstallExtensionWithHostPermissions("Extension", {"<all_urls>"});
  WithholdHostPermissions(extension.get());

  // Navigate to a site and add a site access request for the extension.
  const GURL url("http://www.example.com");
  NavigateAndCommit(url);
  AddHostAccessRequest(*extension,
                       browser()->GetTabStripModel()->GetActiveWebContents());

  // By default, user can customize the site access of each extension and site
  // access will be granted
  ASSERT_EQ(GetUserSiteSetting(url),
            PermissionsManager::UserSiteSetting::kCustomizeByExtension);

  ShowMenu();
  const views::View* reload_section = main_page()->reload_section();
  const views::View* requests_section = main_page()->requests_section();

  // Message section shows request access container with extension.
  LayoutMenuIfNecessary();
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));

  // Message section shows request access container with extension even if
  // requests are not allowed in the toolbar.
  SitePermissionsHelper(profile()).SetShowAccessRequestsInToolbar(
      extension->id(), false);
  EXPECT_FALSE(reload_section->GetVisible());
  EXPECT_TRUE(requests_section->GetVisible());
  EXPECT_THAT(GetExtensionsInRequestsSection(),
              testing::ElementsAre(extension->id()));
}
