// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/simple_test_clock.h"
#include "base/time/clock.h"
#include "build/build_config.h"
#include "chrome/browser/./ui/tabs/tab_enums.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/notifications/notification_interactive_uitest_support.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/content_settings_uma_util.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/history/core/browser/history_service.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "media/base/media_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification_blocker.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/message_center/public/cpp/notification.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
#endif

namespace {

class ToggledNotificationBlocker : public message_center::NotificationBlocker {
 public:
  ToggledNotificationBlocker()
      : message_center::NotificationBlocker(
            message_center::MessageCenter::Get()),
        notifications_enabled_(true) {}
  ToggledNotificationBlocker(const ToggledNotificationBlocker&) = delete;
  ToggledNotificationBlocker& operator=(const ToggledNotificationBlocker&) =
      delete;
  ~ToggledNotificationBlocker() override = default;

  void SetNotificationsEnabled(bool enabled) {
    if (notifications_enabled_ != enabled) {
      notifications_enabled_ = enabled;
      NotifyBlockingStateChanged();
    }
  }

  // NotificationBlocker overrides:
  bool ShouldShowNotificationAsPopup(
      const message_center::Notification& notification) const override {
    return notifications_enabled_;
  }

 private:
  bool notifications_enabled_;
};

#if !BUILDFLAG(IS_ANDROID)
// Browser test class that creates a fake monitor MediaStream device and auto
// selects it when requesting one via navigator.mediaDevices.getDisplayMedia().
class NotificationsTestWithFakeMediaStream : public NotificationsTest {
 public:
  NotificationsTestWithFakeMediaStream() = default;
  ~NotificationsTestWithFakeMediaStream() override = default;

  // InProcessBrowserTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    NotificationsTest::SetUpCommandLine(command_line);
    command_line->RemoveSwitch(switches::kUseFakeDeviceForMediaStream);
    command_line->AppendSwitchASCII(switches::kUseFakeDeviceForMediaStream,
                                    "display-media-type=monitor");
    command_line->AppendSwitchASCII(switches::kAutoSelectDesktopCaptureSource,
                                    "Entire screen");
  }
};
#endif  // !BUILDFLAG(IS_ANDROID)

}  // namespace

namespace {

const char kExpectedIconUrl[] = "/notifications/no_such_file.png";

}  // namespace

// Flaky on Windows, Mac, Linux: http://crbug.com/40395983.
IN_PROC_BROWSER_TEST_F(NotificationsTest, DISABLED_TestUserGestureInfobar) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL(
                     "/notifications/notifications_request_function.html")));

  // Request permission by calling request() while eval'ing an inline script;
  // That's considered a user gesture to webkit, and should produce an infobar.
  EXPECT_EQ(true,
            content::EvalJs(GetActiveWebContents(browser()), "request();"));

  infobars::ContentInfoBarManager* infobar_manager =
      infobars::ContentInfoBarManager::FromWebContents(
          browser()->GetTabStripModel()->GetWebContentsAt(0));
  EXPECT_EQ(1U, infobar_manager->infobars().size());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCreateSimpleNotification) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Creates a simple notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  GURL EXPECTED_ICON_URL = embedded_test_server()->GetURL(kExpectedIconUrl);
  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  EXPECT_EQ(u"My Title", (*notifications.rbegin())->title());
  EXPECT_EQ(u"My Body", (*notifications.rbegin())->message());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, NotificationBlockerTest) {
  ToggledNotificationBlocker blocker;
  blocker.Init();
  TestMessageCenterObserver observer;

  ASSERT_TRUE(embedded_test_server()->Start());
  message_center::MessageCenter::Get()->AddObserver(&observer);

  // Creates a simple notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  EXPECT_EQ(0, GetNotificationPopupCount());
  blocker.SetNotificationsEnabled(false);

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);
  EXPECT_EQ(0, GetNotificationPopupCount());
  EXPECT_EQ("", observer.last_displayed_id());

  blocker.SetNotificationsEnabled(true);
  EXPECT_EQ(1, GetNotificationPopupCount());
  EXPECT_NE("", observer.last_displayed_id());

  result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);
  result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  blocker.SetNotificationsEnabled(false);
  EXPECT_EQ(0, GetNotificationPopupCount());

  message_center::MessageCenter::Get()->RemoveObserver(&observer);
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseNotification) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Creates a notification and closes it.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);
  ASSERT_EQ(1, GetNotificationCount());

  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  message_center::MessageCenter::Get()->RemoveNotification(
      (*notifications.rbegin())->id(),
      true);  // by_user

  ASSERT_EQ(0, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCancelNotification) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Creates a notification and cancels it in the origin page.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string note_id = CreateSimpleNotification(browser(), true);
  EXPECT_NE(note_id, "-1");

  ASSERT_EQ(1, GetNotificationCount());
  ASSERT_TRUE(CancelNotification(note_id.c_str(), browser()));
  ASSERT_EQ(0, GetNotificationCount());
}

// Requests notification privileges and verifies the prompt appears.
IN_PROC_BROWSER_TEST_F(NotificationsTest, TestPermissionRequestUIAppears) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  EXPECT_TRUE(RequestPermissionAndWait(browser()));
  ASSERT_EQ(0, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowOnPermissionRequestUI) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Tries to create a notification & clicks 'allow' on the prompt.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  // This notification should not be shown because we do not have permission.
  CreateSimpleNotification(browser(), false);
  ASSERT_EQ(0, GetNotificationCount());

  ASSERT_TRUE(RequestAndAcceptPermission(browser()));

  CreateSimpleNotification(browser(), true);
  EXPECT_EQ(1, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyOnPermissionRequestUI) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that no notification is created when Deny is chosen from prompt.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  ASSERT_TRUE(RequestAndDenyPermission(browser()));
  CreateSimpleNotification(browser(), false);
  ASSERT_EQ(0, GetNotificationCount());
  ContentSettingsForOneType settings;
  GetDisabledContentSettings(&settings);
  EXPECT_TRUE(CheckOriginInSetting(settings, GetTestPageURL()));
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestClosePermissionRequestUI) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that no notification is created when prompt is dismissed.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  ASSERT_TRUE(RequestAndDismissPermission(browser()));
  CreateSimpleNotification(browser(), false);
  ASSERT_EQ(0, GetNotificationCount());
  ContentSettingsForOneType settings;
  GetDisabledContentSettings(&settings);
  EXPECT_EQ(0U, settings.size());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestPermissionAPI) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that Notification.permission returns the right thing.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  EXPECT_EQ("default", QueryPermissionStatus(browser()));

  AllowOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  EXPECT_EQ("granted", QueryPermissionStatus(browser()));

  DenyOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  EXPECT_EQ("denied", QueryPermissionStatus(browser()));
}

IN_PROC_BROWSER_TEST_F(NotificationsTestWithPermissionsEmbargo,
                       TestPermissionEmbargo) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  // Verify embargo behaviour - automatically blocked after 3 dismisses.
  ASSERT_TRUE(RequestAndDismissPermission(browser()));
  EXPECT_EQ("default", QueryPermissionStatus(browser()));

  ASSERT_TRUE(RequestAndDismissPermission(browser()));
  EXPECT_EQ("default", QueryPermissionStatus(browser()));

  ASSERT_TRUE(RequestAndDismissPermission(browser()));
  EXPECT_EQ("denied", QueryPermissionStatus(browser()));
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowNotificationsFromAllSites) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify that all domains can be allowed to show notifications.
  SetDefaultContentSetting(CONTENT_SETTING_ALLOW);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyNotificationsFromAllSites) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify that no domain can show notifications.
  SetDefaultContentSetting(CONTENT_SETTING_BLOCK);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), false);
  EXPECT_EQ("-1", result);

  ASSERT_EQ(0, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyDomainAndAllowAll) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify that denying a domain and allowing all shouldn't show
  // notifications from the denied domain.
  DenyOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  SetDefaultContentSetting(CONTENT_SETTING_ALLOW);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), false);
  EXPECT_EQ("-1", result);

  ASSERT_EQ(0, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestAllowDomainAndDenyAll) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify that allowing a domain and denying all others should show
  // notifications from the allowed domain.
  AllowOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  SetDefaultContentSetting(CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestDenyAndThenAllowDomain) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify that denying and again allowing should show notifications.
  DenyOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), false);
  EXPECT_EQ("-1", result);

  ASSERT_EQ(0, GetNotificationCount());

  AllowOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, InlinePermissionRevokeUkm) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ukm::TestAutoSetUkmRecorder ukm_recorder;

  // Verify able to create, deny, and close the notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  base::RunLoop origin_queried_waiter;
  ukm_recorder.SetOnAddEntryCallback(ukm::builders::Permission::kEntryName,
                                     origin_queried_waiter.QuitClosure());
  CreateSimpleNotification(browser(), true);
  ASSERT_EQ(1, GetNotificationCount());

  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  message_center::MessageCenter::Get()->DisableNotification(
      (*notifications.rbegin())->id());

  origin_queried_waiter.Run();

  auto entries = ukm_recorder.GetEntriesByName("Permission");
  EXPECT_EQ(1u, entries.size());
  auto* entry = entries.front().get();

  ukm_recorder.ExpectEntrySourceHasUrl(entry,
                                       embedded_test_server()->base_url());
  EXPECT_EQ(
      *ukm_recorder.GetEntryMetric(entry, "Source"),
      static_cast<int64_t>(permissions::PermissionSourceUI::INLINE_SETTINGS));
  EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "PermissionType"),
            content_settings_uma_util::ContentSettingTypeToHistogramValue(
                ContentSettingsType::NOTIFICATIONS));
  EXPECT_EQ(*ukm_recorder.GetEntryMetric(entry, "Action"),
            static_cast<int64_t>(permissions::PermissionAction::REVOKED));
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCreateDenyCloseNotifications) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Verify able to create, deny, and close the notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  CreateSimpleNotification(browser(), true);
  ASSERT_EQ(1, GetNotificationCount());

  DenyOrigin(GetTestPageURL().DeprecatedGetOriginAsURL());
  ContentSettingsForOneType settings;
  GetDisabledContentSettings(&settings);
  ASSERT_TRUE(CheckOriginInSetting(
      settings, GetTestPageURL().DeprecatedGetOriginAsURL()));

  EXPECT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  message_center::MessageCenter::Get()->RemoveNotification(
      (*notifications.rbegin())->id(),
      true);  // by_user
  ASSERT_EQ(0, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseTabWithPermissionRequestUI) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that user can close tab when bubble present.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  EXPECT_TRUE(RequestPermissionAndWait(browser()));
  content::WebContentsDestroyedWatcher destroyed_watcher(
      browser()->GetTabStripModel()->GetWebContentsAt(0));
  browser()->GetTabStripModel()->CloseWebContentsAt(0,
                                                    TabCloseTypes::CLOSE_NONE);
  destroyed_watcher.Wait();
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCrashRendererNotificationRemain) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test crashing renderer does not close or crash notification.
  AllowAllOrigins();
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_BACKGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
  browser()->GetTabStripModel()->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  CreateSimpleNotification(browser(), true);
  ASSERT_EQ(1, GetNotificationCount());
  CrashTab(browser(), 0);
  ASSERT_EQ(1, GetNotificationCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNotificationReplacement) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that we can replace a notification using the replaceId.
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateNotification(
      browser(), true, "abc.png", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());

  result = CreateNotification(
      browser(), false, "no_such_file.png", "Title2", "Body2", "chat");
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  EXPECT_EQ(u"Title2", (*notifications.rbegin())->title());
  EXPECT_EQ(u"Body2", (*notifications.rbegin())->message());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest,
                       TestNotificationReplacementReappearance) {
  message_center::MessageCenter::Get()->SetHasMessageCenterView(false);

  ASSERT_TRUE(embedded_test_server()->Start());

  // Test that we can replace a notification using the tag, and that it will
  // cause the notification to reappear as a popup again.
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  ASSERT_EQ(0, GetNotificationPopupCount());

  std::string result = CreateNotification(
      browser(), true, "abc.png", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationPopupCount());

  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  message_center::MessageCenter::Get()->ClickOnNotification(
      (*notifications.rbegin())->id());

  ASSERT_EQ(1, GetNotificationPopupCount());

  result = CreateNotification(
      browser(), true, "abc.png", "Title2", "Body2", "chat");
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationPopupCount());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNotificationValidIcon) {
  ASSERT_TRUE(embedded_test_server()->Start());
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  ASSERT_EQ(0, GetNotificationPopupCount());

  std::string result = CreateNotification(
      browser(), true, "icon.png", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  message_center::NotificationList::PopupNotifications notifications =
      message_center::MessageCenter::Get()->GetPopupNotifications();
  ASSERT_EQ(1u, notifications.size());

  auto* notification = *notifications.rbegin();

  EXPECT_EQ(100, notification->icon().Size().width());
  EXPECT_EQ(100, notification->icon().Size().height());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNotificationInvalidIcon) {
  ASSERT_TRUE(embedded_test_server()->Start());
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  ASSERT_EQ(0, GetNotificationPopupCount());

  // Not supplying an icon URL.
  std::string result = CreateNotification(
      browser(), true, "", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  message_center::NotificationList::PopupNotifications notifications =
      message_center::MessageCenter::Get()->GetPopupNotifications();
  ASSERT_EQ(1u, notifications.size());

  auto* notification = *notifications.rbegin();
  EXPECT_TRUE(notification->icon().IsEmpty());

  // Supplying an invalid icon URL.
  result = CreateNotification(
      browser(), true, "invalid.png", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  notifications = message_center::MessageCenter::Get()->GetPopupNotifications();
  ASSERT_EQ(1u, notifications.size());

  notification = *notifications.rbegin();
  EXPECT_TRUE(notification->icon().IsEmpty());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestNotificationDoubleClose) {
  ASSERT_TRUE(embedded_test_server()->Start());
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), GetTestPageURLForFile("notification-double-close.html")));
  ASSERT_EQ(0, GetNotificationPopupCount());

  std::string result = CreateNotification(
      browser(), true, "", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  message_center::MessageCenter::Get()->RemoveNotification(
      (*notifications.rbegin())->id(),
      true);  // by_user

  ASSERT_EQ(0, GetNotificationCount());

  // Calling WebContents::IsCrashed() will return FALSE here, even if the WC did
  // crash. Work around this timing issue by creating another notification,
  // which requires interaction with the renderer process.
  result = CreateNotification(browser(), true, "", "Title1", "Body1", "chat");
  EXPECT_NE("-1", result);
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayNormal) {
  ASSERT_TRUE(embedded_test_server()->Start());

  // Creates a simple notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();

  // Because the webpage is not fullscreen, there will be no special fullscreen
  // visibility tagged on the notification.
  EXPECT_EQ(message_center::FullscreenVisibility::NONE,
            (*notifications.rbegin())->fullscreen_visibility());
}

IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayFullscreen) {
#if BUILDFLAG(IS_MAC)
  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
#endif
  ASSERT_TRUE(embedded_test_server()->Start());

  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  // Set the page fullscreen
  ui_test_utils::ToggleFullscreenModeAndWait(browser());

  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
      browser()->GetWindow()->GetNativeWindow()));

  ASSERT_TRUE(browser()->GetWindow()->IsActive());

  // Creates a simple notification.
  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);
  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();

  // Because the webpage is fullscreen, the fullscreen bit will be set.
  EXPECT_EQ(message_center::FullscreenVisibility::OVER_USER,
            (*notifications.rbegin())->fullscreen_visibility());
}

// The Fake OSX fullscreen window doesn't like drawing a second fullscreen
// window when another is visible.
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayMultiFullscreen) {
  ASSERT_TRUE(embedded_test_server()->Start());
  AllowAllOrigins();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  BrowserWindowInterface* other_browser =
      CreateBrowser(browser()->GetProfile());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(other_browser, GURL("about:blank")));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  // Set the notification page fullscreen
  ui_test_utils::ToggleFullscreenModeAndWait(browser());

  // Set the other browser fullscreen
  ui_test_utils::ToggleFullscreenModeAndWait(other_browser);

  ASSERT_TRUE(browser()->GetWindow()->IsFullscreen());
  ASSERT_TRUE(other_browser->GetWindow()->IsFullscreen());

  ui_test_utils::BrowserActivationWaiter waiter(other_browser);
  waiter.WaitForActivation();
  ASSERT_FALSE(browser()->GetWindow()->IsActive());
  ASSERT_TRUE(other_browser->GetWindow()->IsActive());

  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  // Because the second page is the top-most fullscreen, the fullscreen bit
  // won't be set.
  EXPECT_EQ(message_center::FullscreenVisibility::NONE,
            (*notifications.rbegin())->fullscreen_visibility());
}
#endif

// Verify that a notification is actually displayed when the webpage that
// creates it is fullscreen with the fullscreen notification flag turned on.
IN_PROC_BROWSER_TEST_F(NotificationsTest, TestShouldDisplayPopupNotification) {
#if BUILDFLAG(IS_MAC)
  ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
#endif
  ASSERT_TRUE(embedded_test_server()->Start());

  // Creates a simple notification.
  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));

  // Set the page fullscreen
  ui_test_utils::ToggleFullscreenModeAndWait(browser());

  ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(
      browser()->GetWindow()->GetNativeWindow()));

  std::string result = CreateSimpleNotification(browser(), true);
  EXPECT_NE("-1", result);

  ASSERT_EQ(1, GetNotificationCount());
  message_center::NotificationList::PopupNotifications notifications =
      message_center::MessageCenter::Get()->GetPopupNotifications();
  ASSERT_EQ(1u, notifications.size());
}

#if !BUILDFLAG(IS_ANDROID)
// TODO(crbug.com/40721738): Test fails on Windows and macOS on the bots as
// there is no real display to test with. Need to find a way to run these
// without a display. Tests pass locally with a real display.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#define MAYBE_ShouldQueueDuringScreenPresent \
  DISABLED_ShouldQueueDuringScreenPresent
#else
#define MAYBE_ShouldQueueDuringScreenPresent ShouldQueueDuringScreenPresent
#endif
IN_PROC_BROWSER_TEST_F(NotificationsTestWithFakeMediaStream,
                       MAYBE_ShouldQueueDuringScreenPresent) {
  ASSERT_TRUE(embedded_test_server()->Start());
  // Start second server so we can test screen recording on secure connections.
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  ASSERT_TRUE(https_server.Start());

  AllowAllOrigins();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestPageURL()));
  const int notification_tab = browser()->GetTabStripModel()->active_index();

  // We should see displayed notifications by default.
  std::string result = CreateSimpleNotification(browser(), /*wait=*/false);
  EXPECT_NE("-1", result);
  message_center::NotificationList::Notifications notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  ASSERT_EQ(1u, notifications.size());
  EXPECT_EQ(u"My Title", (*notifications.begin())->title());
  EXPECT_EQ(u"My Body", (*notifications.begin())->message());

  // Open a new tab to a diffent origin from the one that shows notifications.
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      https_server.GetURL("/notifications/notification_tester.html")));
  const int screen_capture_tab = browser()->GetTabStripModel()->active_index();

  // Start a screen capture session.
  content::WebContents* web_contents = GetActiveWebContents(browser());
  ASSERT_EQ("success", content::EvalJs(web_contents, "startScreenCapture();"));

  // Showing a notification during the screen capture session should show the
  // "Notifications muted" notification.
  browser()->GetTabStripModel()->ActivateTabAt(notification_tab);
  result = CreateSimpleNotification(browser(), /*wait=*/false);
  EXPECT_NE("-1", result);
  notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  ASSERT_EQ(2u, notifications.size());
  EXPECT_EQ(l10n_util::GetPluralStringFUTF16(IDS_NOTIFICATION_MUTED_TITLE,
                                             /*count=*/1),
            (*notifications.begin())->title());
  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_NOTIFICATION_MUTED_MESSAGE),
            (*notifications.begin())->message());

  // Showing another notification during the screen captuure session should
  // update the "Notifications muted" notification title.
  result = CreateSimpleNotification(browser(), /*wait=*/false);
  EXPECT_NE("-1", result);
  notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  ASSERT_EQ(2u, notifications.size());
  EXPECT_EQ(l10n_util::GetPluralStringFUTF16(IDS_NOTIFICATION_MUTED_TITLE,
                                             /*count=*/2),
            (*notifications.begin())->title());
  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_NOTIFICATION_MUTED_MESSAGE),
            (*notifications.begin())->message());

  // Stop the screen capture session.
  browser()->GetTabStripModel()->ActivateTabAt(screen_capture_tab);
  ASSERT_EQ("success", content::EvalJs(web_contents, "stopScreenCapture();"));

  // Stopping the screen capture session should display the queued notifications
  // and close the "Notifications muted" notification.
  notifications =
      message_center::MessageCenter::Get()->GetVisibleNotifications();
  ASSERT_EQ(3u, notifications.size());
  for (const message_center::Notification* notification : notifications) {
    EXPECT_EQ(u"My Title", notification->title());
    EXPECT_EQ(u"My Body", notification->message());
  }
}
#endif  // !BUILDFLAG(IS_ANDROID)
