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

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <string>

#include "base/barrier_closure.h"
#include "base/base64url.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/gcm/gcm_profile_service_factory.h"
#include "chrome/browser/gcm/instance_id/instance_id_profile_service_factory.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/notification_handler.h"
#include "chrome/browser/permissions/crowd_deny_fake_safe_browsing_database_manager.h"
#include "chrome/browser/permissions/crowd_deny_preload_data.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
#include "chrome/browser/push_messaging/push_messaging_service_factory.h"
#include "chrome/browser/push_messaging/push_messaging_service_impl.h"
#include "chrome/browser/push_messaging/push_messaging_unsubscribed_entry.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/browsing_data/content/browsing_data_helper.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/gcm_driver/common/gcm_message.h"
#include "components/gcm_driver/fake_gcm_profile_service.h"
#include "components/gcm_driver/gcm_client.h"
#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
#include "components/gcm_driver/instance_id/instance_id_driver.h"
#include "components/gcm_driver/instance_id/instance_id_profile_service.h"
#include "components/keep_alive_registry/keep_alive_registry.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/permissions/permission_request_manager.h"
#include "components/push_messaging/app_identifier.h"
#include "components/push_messaging/app_identifier_test_support.h"
#include "components/push_messaging/push_messaging_constants.h"
#include "components/push_messaging/push_messaging_features.h"
#include "components/push_messaging/push_messaging_utils.h"
#include "components/site_engagement/content/site_engagement_score.h"
#include "components/site_engagement/content/site_engagement_service.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom.h"
#include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
#include "ui/base/window_open_disposition.h"
#include "ui/message_center/public/cpp/notification.h"

namespace {

using testing::ElementsAre;
using testing::IsEmpty;
using testing::Property;

const char kManifestSenderId[] = "1234567890";
const int32_t kApplicationServerKeyLength = 65;

enum class PushSubscriptionKeyFormat { kOmitKey, kBinary, kBase64UrlEncoded };

// NIST P-256 public key made available to tests. Must be an uncompressed
// point in accordance with SEC1 2.3.3.
const uint8_t kApplicationServerKey[kApplicationServerKeyLength] = {
    0x04, 0x55, 0x52, 0x6A, 0xA5, 0x6E, 0x8E, 0xAA, 0x47, 0x97, 0x36,
    0x10, 0xC1, 0x66, 0x3C, 0x1E, 0x65, 0xBF, 0xA1, 0x7B, 0xEE, 0x48,
    0xC9, 0xC6, 0xBB, 0xBF, 0x02, 0x18, 0x53, 0x72, 0x1D, 0x0C, 0x7B,
    0xA9, 0xE3, 0x11, 0xB7, 0x03, 0x52, 0x21, 0xD3, 0x71, 0x90, 0x13,
    0xA8, 0xC1, 0xCF, 0xED, 0x20, 0xF7, 0x1F, 0xD1, 0x7F, 0xF2, 0x76,
    0xB6, 0x01, 0x20, 0xD8, 0x35, 0xA5, 0xD9, 0x3C, 0x43, 0xFD};

// URL-safe base64 encoded version of the |kApplicationServerKey|.
const char kEncodedApplicationServerKey[] =
    "BFVSaqVujqpHlzYQwWY8HmW_oXvuSMnGu78CGFNyHQx7qeMRtwNSIdNxkBOowc_tIPcf0X_ydr"
    "YBINg1pdk8Q_0";

// From chrome/browser/push_messaging/push_messaging_manager.cc
const char* kIncognitoWarningPattern =
    "Chrome currently does not support the Push API in incognito mode "
    "(https://crbug.com/41124656). There is deliberately no way to "
    "feature-detect this, since incognito mode needs to be undetectable by "
    "websites.";

std::string GetTestApplicationServerKey(bool base64_url_encoded = false) {
  std::string application_server_key;

  if (base64_url_encoded) {
    base::Base64UrlEncode(reinterpret_cast<const char*>(kApplicationServerKey),
                          base::Base64UrlEncodePolicy::OMIT_PADDING,
                          &application_server_key);
  } else {
    application_server_key = std::string(std::begin(kApplicationServerKey),
                                         std::end(kApplicationServerKey));
  }

  return application_server_key;
}

void LegacyRegisterCallback(base::OnceClosure done_callback,
                            std::string* out_registration_id,
                            gcm::GCMClient::Result* out_result,
                            const std::string& registration_id,
                            gcm::GCMClient::Result result) {
  if (out_registration_id)
    *out_registration_id = registration_id;
  if (out_result)
    *out_result = result;
  std::move(done_callback).Run();
}

void DidRegister(base::OnceClosure done_callback,
                 const std::string& registration_id,
                 const GURL& endpoint,
                 const std::optional<base::Time>& expiration_time,
                 const std::vector<uint8_t>& p256dh,
                 const std::vector<uint8_t>& auth,
                 blink::mojom::PushRegistrationStatus status) {
  EXPECT_EQ(blink::mojom::PushRegistrationStatus::SUCCESS_FROM_PUSH_SERVICE,
            status);
  std::move(done_callback).Run();
}

void InstanceIDResultCallback(base::OnceClosure done_callback,
                              instance_id::InstanceID::Result* out_result,
                              instance_id::InstanceID::Result result) {
  DCHECK(out_result);
  *out_result = result;
  std::move(done_callback).Run();
}

}  // namespace

class PushMessagingBrowserTestBase
    : public InProcessBrowserTest,
      public push_messaging::AppIdentifierTestSupport {
 public:
  PushMessagingBrowserTestBase()
      : scoped_testing_factory_installer_(
            base::BindRepeating(&gcm::FakeGCMProfileService::Build)),
        gcm_service_(nullptr),
        gcm_driver_(nullptr) {}

  ~PushMessagingBrowserTestBase() override = default;

  PushMessagingBrowserTestBase(const PushMessagingBrowserTestBase&) = delete;
  PushMessagingBrowserTestBase& operator=(const PushMessagingBrowserTestBase&) =
      delete;

  // InProcessBrowserTest:
  void SetUp() override {
    https_server_ = std::make_unique<net::EmbeddedTestServer>(
        net::EmbeddedTestServer::TYPE_HTTPS);
    https_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
    // Used for PushMessagingPartitionedBrowserTest.CrossOriginFrame.
    https_server_->SetCertHostnames({"embedder.com", "requester.com"});
    content::SetupCrossSiteRedirector(https_server_.get());

    site_engagement::SiteEngagementScore::SetParamValuesForTesting();
    InProcessBrowserTest::SetUp();
  }
  void SetUpCommandLine(base::CommandLine* command_line) override {
    // Enable experimental features for subscription restrictions.
    command_line->AppendSwitch(
        switches::kEnableExperimentalWebPlatformFeatures);
  }

  // InProcessBrowserTest:
  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    ASSERT_TRUE(https_server_->Start());

    KeyedService* keyed_service = gcm::GCMProfileServiceFactory::GetForProfile(
        GetBrowser()->GetProfile());
    if (keyed_service) {
      gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(keyed_service);
      gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
          gcm_service_->driver());
    }

    notification_tester_ = std::make_unique<NotificationDisplayServiceTester>(
        GetBrowser()->GetProfile());

    push_service_ =
        PushMessagingServiceFactory::GetForProfile(GetBrowser()->GetProfile());

    LoadTestPage();
  }

  void TearDownOnMainThread() override {
    notification_tester_.reset();
    InProcessBrowserTest::TearDownOnMainThread();
  }

  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void RestartPushService() {
    Profile* profile = GetBrowser()->GetProfile();
    PushMessagingServiceFactory::GetInstance()->SetTestingFactory(
        profile, BrowserContextKeyedServiceFactory::TestingFactory());
    ASSERT_EQ(nullptr, PushMessagingServiceFactory::GetForProfile(profile));
    PushMessagingServiceFactory::GetInstance()->RestoreFactoryForTests(profile);
    PushMessagingServiceImpl::InitializeForProfile(profile);
    push_service_ = PushMessagingServiceFactory::GetForProfile(profile);
  }

  // Helper function to test if a Keep Alive is registered while avoiding the
  // platform checks. Returns a boolean so that assertion failures are reported
  // at the right line.
  // Returns true when KeepAlives are not supported by the platform, or when
  // the registration state is equal to the expectation.
  bool IsRegisteredKeepAliveEqualTo(bool expectation) {
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
    return expectation ==
           KeepAliveRegistry::GetInstance()->IsOriginRegistered(
               KeepAliveOrigin::IN_FLIGHT_PUSH_MESSAGE);
#else
    return true;
#endif
  }

  void LoadTestPage(const std::string& path) {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(),
                                             https_server_->GetURL(path)));
  }

  void LoadTestPage() { LoadTestPage(GetTestURL()); }

  void LoadTestPageWithoutManifest() { LoadTestPage(GetNoManifestTestURL()); }

  content::EvalJsResult RunScript(const std::string& script) {
    return RunScript(script, nullptr);
  }

  content::EvalJsResult RunScript(const std::string& script,
                                  content::WebContents* web_contents) {
    if (!web_contents) {
      web_contents = GetBrowser()->GetTabStripModel()->GetActiveWebContents();
    }
    return content::EvalJs(web_contents->GetPrimaryMainFrame(), script);
  }

  gcm::GCMAppHandler* GetAppHandler() {
    return gcm_driver_->GetAppHandler(push_messaging::kAppIdentifierPrefix);
  }

  permissions::PermissionRequestManager* GetPermissionRequestManager() {
    return permissions::PermissionRequestManager::FromWebContents(
        GetBrowser()->GetTabStripModel()->GetActiveWebContents());
  }

  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void RequestAndAcceptPermission();
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void RequestAndDenyPermission();

  // Sets out_token to the subscription token (not including server URL).
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void SubscribeSuccessfully(
      PushSubscriptionKeyFormat key_format = PushSubscriptionKeyFormat::kBinary,
      std::string* out_token = nullptr);

  // Sets up the state corresponding to a dangling push subscription whose
  // service worker registration no longer exists. Some users may be left with
  // such orphaned subscriptions due to service worker unregistrations not
  // clearing push subscriptions in the past. This allows us to emulate that.
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void SetupOrphanedPushSubscription(std::string* out_app_id);

  // Legacy subscribe path using GCMDriver rather than Instance IDs. Only
  // for testing that we maintain support for existing stored registrations.
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void LegacySubscribeSuccessfully(std::string* out_subscription_id = nullptr);

  // Strips server URL from a registration endpoint to get subscription token.
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void EndpointToToken(const std::string& endpoint,
                       bool standard_protocol = true,
                       std::string* out_token = nullptr);

  blink::mojom::PushSubscriptionPtr GetSubscriptionForAppIdentifier(
      const push_messaging::AppIdentifier& app_identifier) {
    blink::mojom::PushSubscriptionPtr result;
    base::RunLoop run_loop;
    push_service_->GetPushSubscriptionFromAppIdentifier(
        app_identifier,
        base::BindLambdaForTesting(
            [&](blink::mojom::PushSubscriptionPtr subscription) {
              result = std::move(subscription);
              run_loop.Quit();
            }));
    run_loop.Run();
    return result;
  }

  // Deletes an Instance ID from the GCM Store but keeps the push subscription
  // stored in the push_messaging::AppIdentifier map and Service Worker DB.
  // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
  void DeleteInstanceIDAsIfGCMStoreReset(const std::string& app_id);

  push_messaging::AppIdentifier GetAppIdentifierForServiceWorkerRegistration(
      int64_t service_worker_registration_id);

  void SendMessageAndWaitUntilHandled(
      const push_messaging::AppIdentifier& app_identifier,
      const gcm::IncomingMessage& message);

  net::EmbeddedTestServer* https_server() const { return https_server_.get(); }

  // Returns a vector of the currently displayed Notification objects.
  std::vector<message_center::Notification> GetDisplayedNotifications() {
    return notification_tester_->GetDisplayedNotificationsForType(
        NotificationHandler::Type::WEB_PERSISTENT);
  }

  // Returns the number of notifications that are currently being shown.
  size_t GetNotificationCount() { return GetDisplayedNotifications().size(); }

  // Removes all shown notifications.
  void RemoveAllNotifications() {
    notification_tester_->RemoveAllNotifications(
        NotificationHandler::Type::WEB_PERSISTENT, true /* by_user */);
  }

  // To be called when delivery of a push message has finished. The |run_loop|
  // will be told to quit after |messages_required| messages were received.
  void OnDeliveryFinished(std::vector<size_t>* number_of_notifications_shown,
                          base::OnceClosure done_closure) {
    DCHECK(number_of_notifications_shown);
    number_of_notifications_shown->push_back(GetNotificationCount());

    std::move(done_closure).Run();
  }

  PushMessagingServiceImpl* push_service() const { return push_service_; }

  void SetSiteEngagementScore(const GURL& url, double score) {
    site_engagement::SiteEngagementService* service =
        site_engagement::SiteEngagementService::Get(GetBrowser()->GetProfile());
    service->ResetBaseScoreForURL(url, score);
    EXPECT_EQ(score, service->GetScore(url));
  }

  // Matches |tag| against the notification's ID to see if the notification's
  // js-provided tag could have been |tag|. This is not perfect as it might
  // return true for a |tag| that is a substring of the original tag.
  static bool TagEquals(const message_center::Notification& notification,
                        const std::string& tag) {
    return std::string::npos != notification.id().find(tag);
  }

 protected:
  virtual std::string GetTestURL() { return "/push_messaging/test.html"; }

  virtual std::string GetNoManifestTestURL() {
    return "/push_messaging/test_no_manifest.html";
  }

  virtual BrowserWindowInterface* GetBrowser() const { return browser(); }

  gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
      scoped_testing_factory_installer_;

  raw_ptr<gcm::FakeGCMProfileService, DanglingUntriaged> gcm_service_;
  raw_ptr<instance_id::FakeGCMDriverForInstanceID, DanglingUntriaged>
      gcm_driver_;
  base::HistogramTester histogram_tester_;

  std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;

 private:
  std::unique_ptr<net::EmbeddedTestServer> https_server_;
  raw_ptr<PushMessagingServiceImpl, DanglingUntriaged> push_service_;
};

void PushMessagingBrowserTestBase::RequestAndAcceptPermission() {
  GetPermissionRequestManager()->set_auto_response_for_test(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  ASSERT_EQ("permission status - granted",
            RunScript("requestNotificationPermission();"));
}

void PushMessagingBrowserTestBase::RequestAndDenyPermission() {
  GetPermissionRequestManager()->set_auto_response_for_test(
      permissions::PermissionRequestManager::DENY_ALL);
  ASSERT_EQ("permission status - denied",
            RunScript("requestNotificationPermission();"));
}

void PushMessagingBrowserTestBase::SubscribeSuccessfully(
    PushSubscriptionKeyFormat key_format,
    std::string* out_token) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  switch (key_format) {
    case PushSubscriptionKeyFormat::kBinary:
      ASSERT_EQ("manifest removed", RunScript("removeManifest()"));

      ASSERT_NO_FATAL_FAILURE(
          EndpointToToken(RunScript("documentSubscribePush()").ExtractString(),
                          true, out_token));
      break;
    case PushSubscriptionKeyFormat::kBase64UrlEncoded:
      ASSERT_EQ("manifest removed", RunScript("removeManifest()"));

      ASSERT_NO_FATAL_FAILURE(EndpointToToken(
          RunScript("documentSubscribePushWithBase64URLEncodedString()")
              .ExtractString(),
          true, out_token));
      break;
    case PushSubscriptionKeyFormat::kOmitKey:
      // Test backwards compatibility with old ID based subscriptions.
      ASSERT_NO_FATAL_FAILURE(EndpointToToken(
          RunScript("documentSubscribePushWithoutKey()").ExtractString(), false,
          out_token));
      break;
    default:
      NOTREACHED();
  }
}

void PushMessagingBrowserTestBase::SetupOrphanedPushSubscription(
    std::string* out_app_id) {
  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
  GURL requesting_origin =
      https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  // Use 1234LL as it's unlikely to collide with an active service worker
  // registration id (they increment from 0).
  const int64_t service_worker_registration_id = 1234LL;

  auto options = blink::mojom::PushSubscriptionOptions::New();
  options->user_visible_only = true;

  std::string test_application_server_key = GetTestApplicationServerKey();
  options->application_server_key = std::vector<uint8_t>(
      test_application_server_key.begin(), test_application_server_key.end());

  base::RunLoop run_loop;
  push_service()->SubscribeFromWorker(
      requesting_origin, service_worker_registration_id,
      /*render_process_id=*/-1, std::move(options),
      base::BindOnce(&DidRegister, run_loop.QuitClosure()));
  run_loop.Run();

  push_messaging::AppIdentifier app_identifier =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), requesting_origin,
          service_worker_registration_id);
  ASSERT_FALSE(app_identifier.is_null());
  *out_app_id = app_identifier.app_id();
}

void PushMessagingBrowserTestBase::LegacySubscribeSuccessfully(
    std::string* out_subscription_id) {
  // Create a non-InstanceID GCM registration. Have to directly access
  // GCMDriver, since this codepath has been deleted from Push.

  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  GURL requesting_origin =
      https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  int64_t service_worker_registration_id = 0LL;
  push_messaging::AppIdentifier app_identifier = LegacyGenerateForTesting(
      requesting_origin, service_worker_registration_id);
  push_service_->IncreasePushSubscriptionCount(1, true /* is_pending */);

  std::string subscription_id;
  {
    base::RunLoop run_loop;
    gcm::GCMClient::Result register_result = gcm::GCMClient::UNKNOWN_ERROR;
    gcm_driver_->Register(
        app_identifier.app_id(), {kManifestSenderId},
        base::BindOnce(&LegacyRegisterCallback, run_loop.QuitClosure(),
                       &subscription_id, &register_result));
    run_loop.Run();
    ASSERT_EQ(gcm::GCMClient::SUCCESS, register_result);
  }

  PushMessagingAppIdentifier::PersistToPrefs(app_identifier,
                                             GetBrowser()->GetProfile());
  push_service_->IncreasePushSubscriptionCount(1, false /* is_pending */);
  push_service_->DecreasePushSubscriptionCount(1, true /* was_pending */);

  {
    base::RunLoop run_loop;
    push_service_->StorePushSubscriptionForTesting(
        GetBrowser()->GetProfile(), requesting_origin,
        service_worker_registration_id, subscription_id, kManifestSenderId,
        run_loop.QuitClosure());
    run_loop.Run();
  }

  if (out_subscription_id)
    *out_subscription_id = subscription_id;
}

void PushMessagingBrowserTestBase::EndpointToToken(const std::string& endpoint,
                                                   bool standard_protocol,
                                                   std::string* out_token) {
  size_t last_slash = endpoint.rfind('/');
  ASSERT_NE(last_slash, std::string::npos);

  ASSERT_EQ(push_messaging::GetGcmEndpointForChannel(chrome::GetChannel()),
            endpoint.substr(0, last_slash + 1));
  if (base::FeatureList::IsEnabled(features::kPushMessagingGcmEndpointWebpushPath)) {
    ASSERT_TRUE(endpoint.substr(0, last_slash + 1) == kPushMessagingWebpushEndpoint ||
                endpoint.substr(0, last_slash + 1) == kPushMessagingStagingWebpushEndpoint);
  } else {
    ASSERT_TRUE(endpoint.substr(0, last_slash + 1) == kPushMessagingGcmEndpoint ||
                endpoint.substr(0, last_slash + 1) == kPushMessagingStagingGcmEndpoint);
  }

  ASSERT_LT(last_slash + 1, endpoint.length());  // Token must not be empty.

  if (out_token)
    *out_token = endpoint.substr(last_slash + 1);
}

push_messaging::AppIdentifier
PushMessagingBrowserTestBase::GetAppIdentifierForServiceWorkerRegistration(
    int64_t service_worker_registration_id) {
  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  push_messaging::AppIdentifier app_identifier =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin, service_worker_registration_id);
  EXPECT_FALSE(app_identifier.is_null());
  return app_identifier;
}

void PushMessagingBrowserTestBase::DeleteInstanceIDAsIfGCMStoreReset(
    const std::string& app_id) {
  // Delete the Instance ID directly, keeping the push subscription stored in
  // the push_messaging::AppIdentifier map and the Service Worker database. This
  // simulates the GCM Store getting reset but failing to clear push
  // subscriptions, either because the store got reset before
  // 93ec793ac69a542b2213297737178a55d069fd0d (Chrome 56), or because a race
  // condition (e.g. shutdown) prevents PushMessagingServiceImpl::OnStoreReset
  // from clearing all subscriptions.
  instance_id::InstanceIDProfileService* instance_id_profile_service =
      instance_id::InstanceIDProfileServiceFactory::GetForProfile(
          GetBrowser()->GetProfile());
  DCHECK(instance_id_profile_service);
  instance_id::InstanceIDDriver* instance_id_driver =
      instance_id_profile_service->driver();
  DCHECK(instance_id_driver);
  instance_id::InstanceID::Result delete_result =
      instance_id::InstanceID::UNKNOWN_ERROR;
  base::RunLoop run_loop;
  instance_id_driver->GetInstanceID(app_id)->DeleteID(base::BindOnce(
      &InstanceIDResultCallback, run_loop.QuitClosure(), &delete_result));
  run_loop.Run();
  ASSERT_EQ(instance_id::InstanceID::SUCCESS, delete_result);
}

void PushMessagingBrowserTestBase::SendMessageAndWaitUntilHandled(
    const push_messaging::AppIdentifier& app_identifier,
    const gcm::IncomingMessage& message) {
  base::RunLoop run_loop;
  push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
  push_service()->OnMessage(app_identifier.app_id(), message);
  run_loop.Run();
}

class PushMessagingBrowserTest : public PushMessagingBrowserTestBase {
 public:
  PushMessagingBrowserTest() {
    disabled_features_.push_back(features::kPushMessagingDisallowSenderIDs);
  }

  void SetUp() override {
    feature_list_.InitWithFeatures(enabled_features_, disabled_features_);
    PushMessagingBrowserTestBase::SetUp();
  }

 protected:
  std::vector<base::test::FeatureRef> enabled_features_{};
  std::vector<base::test::FeatureRef> disabled_features_{};

 private:
  base::test::ScopedFeatureList feature_list_;
};

// This class is used to execute PushMessagingBrowserTest tests with
// third-party storage partitioning both enabled/disabled.
class PushMessagingPartitionedBrowserTest
    : public PushMessagingBrowserTest,
      public testing::WithParamInterface<bool> {
 public:
  PushMessagingPartitionedBrowserTest() {
    if (GetParam()) {
      enabled_features_.push_back(
          net::features::kThirdPartyStoragePartitioning);
    } else {
      disabled_features_.push_back(
          net::features::kThirdPartyStoragePartitioning);
    }
  }
};

INSTANTIATE_TEST_SUITE_P(PushMessagingPartitionedBrowserTest,
                         PushMessagingPartitionedBrowserTest,
                         testing::Values(true, false));

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeWithoutKeySuccessNotificationsGranted) {
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey));
  EXPECT_EQ(kManifestSenderId, gcm_driver_->last_gettoken_authorized_entity());
  EXPECT_EQ(GetAppIdentifierForServiceWorkerRegistration(0LL).app_id(),
            gcm_driver_->last_gettoken_app_id());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeSuccessNotificationsGranted) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  EXPECT_EQ(kEncodedApplicationServerKey,
            gcm_driver_->last_gettoken_authorized_entity());
  EXPECT_EQ(GetAppIdentifierForServiceWorkerRegistration(0LL).app_id(),
            gcm_driver_->last_gettoken_app_id());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeSuccessNotificationsGrantedWithBase64URLKey) {
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBase64UrlEncoded));
  EXPECT_EQ(kEncodedApplicationServerKey,
            gcm_driver_->last_gettoken_authorized_entity());
  EXPECT_EQ(GetAppIdentifierForServiceWorkerRegistration(0LL).app_id(),
            gcm_driver_->last_gettoken_app_id());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeSuccessNotificationsPrompt) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  GetPermissionRequestManager()->set_auto_response_for_test(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  // Both of these methods EXPECT that they succeed.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));
  GetAppIdentifierForServiceWorkerRegistration(0LL);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeFailureNotificationsBlocked) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());

  EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
            RunScript("documentSubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoManifest) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  ASSERT_EQ("manifest removed", RunScript("removeManifest()"));

  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, and "
      "manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoSenderId) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  ASSERT_EQ("sender id removed from manifest",
            RunScript("swapManifestNoSenderId()"));

  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, and "
      "gcm_sender_id not found in manifest",
      RunScript("documentSubscribePushWithoutKey()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       RegisterFailureEmptyPushSubscriptionOptions) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
            RunScript("documentSubscribePushWithEmptyOptions()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWithInvalidation) {
  std::string token1, token2, token3;

  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token1));
  ASSERT_FALSE(token1.empty());

  // Repeated calls to |subscribe()| should yield the same token.
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token2));
  ASSERT_EQ(token1, token2);

  push_messaging::AppIdentifier app_identifier =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(),
          https_server()->GetURL("/").DeprecatedGetOriginAsURL(),
          0LL /* service_worker_registration_id */);

  ASSERT_FALSE(app_identifier.is_null());
  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  // Delete the InstanceID. This captures two scenarios: either the database was
  // corrupted, or the subscription was invalidated by the server.
  ASSERT_NO_FATAL_FAILURE(
      DeleteInstanceIDAsIfGCMStoreReset(app_identifier.app_id()));

  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());

  // Repeated calls to |subscribe()| will now (silently) result in a new token.
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token3));
  ASSERT_FALSE(token3.empty());
  EXPECT_NE(token1, token3);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeWorker) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Try to subscribe from a worker without a key. This should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, and "
      "gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));

  // Now run the subscribe with a key. This should succeed.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
                      true /* standard_protocol */));

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       SubscribeWorkerWithBase64URLEncodedString) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Try to subscribe from a worker without a key. This should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, and "
      "gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));

  // Now run the subscribe with a key. This should succeed.
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("workerSubscribePushWithBase64URLEncodedString()")
          .ExtractString(),
      true /* standard_protocol */));

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       ResubscribeWithoutKeyAfterSubscribingWithKeyInManifest) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscription from the document without a key, this will trigger
  // the code to read sender id from the manifest and will write it to the
  // datastore.
  std::string token1;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("documentSubscribePushWithoutKey()").ExtractString(),
      false /* standard_protocol */, &token1));

  ASSERT_EQ("manifest removed", RunScript("removeManifest()"));

  // Try to resubscribe from the document without a key or manifest.
  // This should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));

  // Now run the subscribe from the service worker without a key.
  // In this case, the sender id should be read from the datastore.
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token2));
  EXPECT_EQ(token1, token2);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // After unsubscribing, subscribe again from the worker with no key.
  // The sender id should again be read from the datastore, so the
  // subcribe should succeed, and we should get a new subscription token.
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token3));
  EXPECT_NE(token1, token3);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ResubscribeWithoutKeyAfterSubscribingFromDocumentWithP256Key) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPageWithoutManifest();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscription from the document with a key.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));

  // Try to resubscribe from the document without a key - should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));

  // Now try to resubscribe from the service worker without a key.
  // This should also fail as the original key was not numeric.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // After unsubscribing, try to resubscribe again without a key.
  // This should again fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));
}

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ResubscribeWithoutKeyAfterSubscribingFromWorkerWithP256Key) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPageWithoutManifest();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscribe from the service worker with a key.
  // This should succeed.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
                      true /* standard_protocol */));

  // Try to resubscribe from the document without a key - should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));

  // Now try to resubscribe from the service worker without a key.
  // This should also fail as the original key was not numeric.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, and "
      "gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // After unsubscribing, try to resubscribe again without a key.
  // This should again fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and gcm_sender_id not found in manifest",
      RunScript("workerSubscribePushNoKey()"));
}

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ResubscribeWithoutKeyAfterSubscribingFromDocumentWithNumber) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPageWithoutManifest();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscribe from the document with a numeric key.
  // This should succeed.

  std::string token1;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("documentSubscribePushWithNumericKey()").ExtractString(),
      false /* standard_protocol */, &token1));

  // Try to resubscribe from the document without a key - should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));

  // Now run the subscribe from the service worker without a key.
  // In this case, the sender id should be read from the datastore.
  // Note, we would rather this failed as we only really want to support
  // no-key subscribes after subscribing with a numeric gcm sender id in the
  // manifest, not a numeric applicationServerKey, but for code simplicity
  // this case is allowed.
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token2));
  EXPECT_EQ(token1, token2);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // After unsubscribing, subscribe again from the worker with no key.
  // The sender id should again be read from the datastore, so the
  // subcribe should succeed, and we should get a new subscription token.
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token3));
  EXPECT_NE(token1, token3);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ResubscribeWithoutKeyAfterSubscribingFromWorkerWithNumber) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPageWithoutManifest();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscribe from the service worker with a numeric key.
  // This should succeed.
  std::string token1;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("workerSubscribePushWithNumericKey()").ExtractString(),
      false /* standard_protocol */, &token1));

  // Try to resubscribe from the document without a key - should fail.
  EXPECT_EQ(
      "AbortError - Registration failed - missing applicationServerKey, "
      "and manifest empty or missing",
      RunScript("documentSubscribePushWithoutKey()"));

  // Now run the subscribe from the service worker without a key.
  // In this case, the sender id should be read from the datastore.
  // Note, we would rather this failed as we only really want to support
  // no-key subscribes after subscribing with a numeric gcm sender id in the
  // manifest, not a numeric applicationServerKey, but for code simplicity
  // this case is allowed.
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token2));
  EXPECT_EQ(token1, token2);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // After unsubscribing, subscribe again from the worker with no key.
  // The sender id should again be read from the datastore, so the
  // subcribe should succeed, and we should get a new subscription token.
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePushNoKey()").ExtractString(),
                      false /* standard_protocol */, &token3));
  EXPECT_NE(token1, token3);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ResubscribeWithMismatchedKey) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Run the subscribe from the service worker with a key.
  // This should succeed.
  std::string token1;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("workerSubscribePushWithNumericKey('11111')").ExtractString(),
      false /* standard_protocol */, &token1));

  // Try to resubscribe with a different key - should fail.
  EXPECT_EQ(
      "InvalidStateError - Registration failed - A subscription with a "
      "different applicationServerKey (or gcm_sender_id) already exists; to "
      "change the applicationServerKey, unsubscribe then resubscribe.",
      RunScript("workerSubscribePushWithNumericKey('22222')"));

  // Try to resubscribe with the original key - should succeed.
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("workerSubscribePushWithNumericKey('11111')").ExtractString(),
      false /* standard_protocol */, &token2));
  EXPECT_EQ(token1, token2);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // Resubscribe with a different key after unsubscribing.
  // Should succeed, and we should get a new subscription token.
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("workerSubscribePushWithNumericKey('22222')").ExtractString(),
      false /* standard_protocol */, &token3));
  EXPECT_NE(token1, token3);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribePersisted) {
  // First, test that Service Worker registration IDs are assigned in order of
  // registering the Service Workers, and the (fake) push subscription ids are
  // assigned in order of push subscription (even when these orders are
  // different).

  std::string token1;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token1));
  push_messaging::AppIdentifier sw0_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);
  EXPECT_EQ(sw0_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  LoadTestPage("/push_messaging/subscope1/test.html");
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  LoadTestPage("/push_messaging/subscope2/test.html");
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  // Note that we need to reload the page after registering, otherwise
  // navigator.serviceWorker.ready is going to be resolved with the parent
  // Service Worker which still controls the page.
  LoadTestPage("/push_messaging/subscope2/test.html");
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token2));
  EXPECT_NE(token1, token2);
  push_messaging::AppIdentifier sw2_identifier =
      GetAppIdentifierForServiceWorkerRegistration(2LL);
  EXPECT_EQ(sw2_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  LoadTestPage("/push_messaging/subscope1/test.html");
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token3));
  EXPECT_NE(token1, token3);
  EXPECT_NE(token2, token3);
  push_messaging::AppIdentifier sw1_identifier =
      GetAppIdentifierForServiceWorkerRegistration(1LL);
  EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  // Now test that the Service Worker registration IDs and push subscription IDs
  // generated above were persisted to SW storage, by checking that they are
  // unchanged despite requesting them in a different order.

  LoadTestPage("/push_messaging/subscope1/test.html");
  std::string token4;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token4));
  EXPECT_EQ(token3, token4);
  EXPECT_EQ(sw1_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  LoadTestPage("/push_messaging/subscope2/test.html");
  std::string token5;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token5));
  EXPECT_EQ(token2, token5);
  EXPECT_EQ(sw2_identifier.app_id(), gcm_driver_->last_gettoken_app_id());

  LoadTestPage();
  std::string token6;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token6));
  EXPECT_EQ(token1, token6);
  EXPECT_EQ(sw0_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, AppHandlerOnlyIfSubscribed) {
  // This test restarts the push service to simulate restarting the browser.

  EXPECT_NE(push_service(), GetAppHandler());
  ASSERT_NO_FATAL_FAILURE(RestartPushService());
  EXPECT_NE(push_service(), GetAppHandler());

  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ(push_service(), GetAppHandler());
  ASSERT_NO_FATAL_FAILURE(RestartPushService());
  EXPECT_EQ(push_service(), GetAppHandler());

  // Unsubscribe.
  base::RunLoop run_loop;
  push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  // The app handler is only guaranteed to be unregistered once the unsubscribe
  // callback for testing has been run (PushSubscription.unsubscribe() usually
  // resolves before that, in order to avoid blocking on network retries etc).
  run_loop.Run();

  EXPECT_NE(push_service(), GetAppHandler());
  ASSERT_NO_FATAL_FAILURE(RestartPushService());
  EXPECT_NE(push_service(), GetAppHandler());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventSuccess) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(blink::mojom::PushEventStatus::SUCCESS), 1);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventOnShutdown) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  push_service()->OnAppTerminating();
  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPayload) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.decrypted = false;

  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_EQ("[NULL]", RunScript("resultQueue.pop()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, LegacyPushEvent) {
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  gcm::IncomingMessage message;
  message.sender_id = kManifestSenderId;
  message.decrypted = false;

  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_EQ("[NULL]", RunScript("resultQueue.pop()"));
}

// Some users may have gotten into a state in the past where they still have
// a subscription even though the service worker was unregistered.
// Emulate this and test a push message triggers unsubscription.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventNoServiceWorker) {
  std::string app_id;
  ASSERT_NO_FATAL_FAILURE(SetupOrphanedPushSubscription(&app_id));

  // Try to send a push message.
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;

  base::RunLoop run_loop;
  push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  push_service()->OnMessage(app_id, message);
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
  run_loop.Run();
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));

  // No push data should have been received.
  EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(blink::mojom::PushEventStatus::NO_SERVICE_WORKER), 1);

  // Missing Service Workers should trigger an automatic unsubscription attempt.
  EXPECT_EQ(app_id, gcm_driver_->last_deletetoken_app_id());
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::DELIVERY_NO_SERVICE_WORKER),
      1);

  // |app_identifier| should no longer be stored in prefs.
  push_messaging::AppIdentifier stored_app_identifier =
      PushMessagingAppIdentifier::FindByAppId(GetBrowser()->GetProfile(),
                                              app_id);
  EXPECT_TRUE(stored_app_identifier.is_null());
}

// Tests receiving messages for a subscription that no longer exists.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, NoSubscription) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      1);

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  SendMessageAndWaitUntilHandled(app_identifier, message);

  // No push data should have been received.
  EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(blink::mojom::PushEventStatus::UNKNOWN_APP_ID), 1);

  // Missing subscriptions should trigger an automatic unsubscription attempt.
  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());
  histogram_tester_.ExpectBucketCount(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::DELIVERY_UNKNOWN_APP_ID),
      1);
}

// Tests receiving messages for an origin that does not have permission, but
// somehow still has a subscription (as happened in https://crbug.com/40084988).
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Revoke notifications permission, but first disable the
  // PushMessagingServiceImpl's OnContentSettingChanged handler so that it
  // doesn't automatically unsubscribe, since we want to test the case where
  // there is still a subscription.
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->RemoveObserver(push_service());
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);
  base::RunLoop().RunUntilIdle();

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  SendMessageAndWaitUntilHandled(app_identifier, message);

  // No push data should have been received.
  EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(blink::mojom::PushEventStatus::PERMISSION_DENIED), 1);

  // Missing permission should trigger an automatic unsubscription attempt.
  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  push_messaging::AppIdentifier app_identifier_afterwards =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin, 0LL);
  EXPECT_TRUE(app_identifier_afterwards.is_null());
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::DELIVERY_PERMISSION_DENIED),
      1);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       PushEventEnforcesUserVisibleNotification) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  RemoveAllNotifications();
  ASSERT_EQ(0u, GetNotificationCount());

  // We'll need to specify the web_contents in which to eval script, since we're
  // going to run script in a background tab.
  content::WebContents* web_contents =
      GetBrowser()->GetTabStripModel()->GetActiveWebContents();

  // Set the site engagement score for the site. Setting it to 10 means it
  // should have a budget of 4, enough for two non-shown notification, which
  // cost 2 each.
  SetSiteEngagementScore(web_contents->GetLastCommittedURL(), 10.0);

  // If the site is visible in an active tab, we should not force a notification
  // to be shown. Try it twice, since we allow one mistake per 10 push events.
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.decrypted = true;
  for (int n = 0; n < 2; n++) {
    message.raw_data = "testdata";
    SendMessageAndWaitUntilHandled(app_identifier, message);
    EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));
    EXPECT_EQ(0u, GetNotificationCount());
  }

  // Open a blank foreground tab so site is no longer visible.
  ui_test_utils::NavigateToURLWithDisposition(
      GetBrowser(), GURL("about:blank"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);

  // If the Service Worker push event handler shows a notification, we
  // should not show a forced one.
  message.raw_data = "shownotification";
  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("shownotification", RunScript("resultQueue.pop()", web_contents));
  EXPECT_EQ(1u, GetNotificationCount());
  EXPECT_TRUE(TagEquals(GetDisplayedNotifications()[0], "push_test_tag"));
  RemoveAllNotifications();

  // If the Service Worker push event handler does not show a notification, we
  // should show a forced one, but only once the origin is out of budget.
  message.raw_data = "testdata";
  for (int n = 0; n < 2; n++) {
    // First two missed notifications shouldn't force a default one.
    SendMessageAndWaitUntilHandled(app_identifier, message);
    EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
    EXPECT_EQ(0u, GetNotificationCount());
  }

  // Third missed notification should trigger a default notification, since the
  // origin will be out of budget.
  message.raw_data = "testdata";
  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));

  {
    std::vector<message_center::Notification> notifications =
        GetDisplayedNotifications();
    ASSERT_EQ(notifications.size(), 1u);

    EXPECT_TRUE(
        TagEquals(notifications[0], kPushMessagingForcedNotificationTag));
    EXPECT_TRUE(notifications[0].silent());
  }

  // The notification will be automatically dismissed when the developer shows
  // a new notification themselves at a later point in time.
  base::RunLoop notification_closed_run_loop;
  notification_tester_->SetNotificationClosedClosure(
      notification_closed_run_loop.QuitClosure());

  message.raw_data = "shownotification";
  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("shownotification", RunScript("resultQueue.pop()", web_contents));

  // Wait for the default notification to dismiss.
  notification_closed_run_loop.Run();

  {
    std::vector<message_center::Notification> notifications =
        GetDisplayedNotifications();
    ASSERT_EQ(notifications.size(), 1u);

    EXPECT_FALSE(
        TagEquals(notifications[0], kPushMessagingForcedNotificationTag));
  }
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       PushEventAllowSilentPushCommandLineFlag) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);
  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
  EXPECT_EQ(kEncodedApplicationServerKey,
            gcm_driver_->last_gettoken_authorized_entity());

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  RemoveAllNotifications();
  ASSERT_EQ(0u, GetNotificationCount());

  // We'll need to specify the web_contents in which to eval script, since we're
  // going to run script in a background tab.
  content::WebContents* web_contents =
      GetBrowser()->GetTabStripModel()->GetActiveWebContents();

  SetSiteEngagementScore(web_contents->GetLastCommittedURL(), 5.0);

  ui_test_utils::NavigateToURLWithDisposition(
      GetBrowser(), GURL("about:blank"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);

  // Send a missed notification to use up the budget.
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;

  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));
  EXPECT_EQ(0u, GetNotificationCount());

  // If the Service Worker push event handler does not show a notification, we
  // should show a forced one providing there is no foreground tab and the
  // origin ran out of budget.
  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));

  // Because the --allow-silent-push command line flag has not been passed,
  // this should have shown a default notification.
  {
    std::vector<message_center::Notification> notifications =
        GetDisplayedNotifications();
    ASSERT_EQ(notifications.size(), 1u);

    EXPECT_TRUE(
        TagEquals(notifications[0], kPushMessagingForcedNotificationTag));
    EXPECT_TRUE(notifications[0].silent());
  }

  RemoveAllNotifications();

  // Send the message again, but this time with the -allow-silent-push command
  // line flag set. The default notification should *not* be shown.
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kAllowSilentPush);

  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()", web_contents));

  ASSERT_EQ(0u, GetNotificationCount());
}

class PushMessagingBrowserTestWithAbusiveOriginPermissionRevocation
    : public PushMessagingBrowserTestBase {
 public:
  PushMessagingBrowserTestWithAbusiveOriginPermissionRevocation() = default;

  using SiteReputation = CrowdDenyPreloadData::SiteReputation;

  void CreatedBrowserMainParts(
      content::BrowserMainParts* browser_main_parts) override {
    PushMessagingBrowserTestBase::CreatedBrowserMainParts(browser_main_parts);

    testing_preload_data_.emplace();
    fake_database_manager_ =
        base::MakeRefCounted<CrowdDenyFakeSafeBrowsingDatabaseManager>();
    test_safe_browsing_factory_ =
        std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>();
    test_safe_browsing_factory_->SetTestDatabaseManager(
        fake_database_manager_.get());
    safe_browsing::SafeBrowsingServiceInterface::RegisterFactory(
        test_safe_browsing_factory_.get());
  }

  void AddToPreloadDataBlocklist(
      const GURL& origin,
      chrome_browser_crowd_deny::
          SiteReputation_NotificationUserExperienceQuality reputation_type) {
    SiteReputation reputation;
    reputation.set_notification_ux_quality(reputation_type);
    testing_preload_data_->SetOriginReputation(url::Origin::Create(origin),
                                               std::move(reputation));
  }

  void AddToSafeBrowsingBlocklist(const GURL& url) {
    fake_database_manager_->SetSimulatedVerdictForUrl(url, /*is_abusive=*/true);
  }

 private:
  base::test::ScopedFeatureList feature_list_;
  std::optional<testing::ScopedCrowdDenyPreloadDataOverride>
      testing_preload_data_;
  scoped_refptr<CrowdDenyFakeSafeBrowsingDatabaseManager>
      fake_database_manager_;
  std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory>
      test_safe_browsing_factory_;
};

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTestWithAbusiveOriginPermissionRevocation,
    PushEventPermissionRevoked) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Add an origin to blocking lists after service worker is registered.
  AddToPreloadDataBlocklist(
      https_server()->GetURL("/").DeprecatedGetOriginAsURL(),
      SiteReputation::ABUSIVE_CONTENT);
  AddToSafeBrowsingBlocklist(
      https_server()->GetURL("/").DeprecatedGetOriginAsURL());

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  SendMessageAndWaitUntilHandled(app_identifier, message);

  // No push data should have been received.
  EXPECT_EQ("null", RunScript("String(resultQueue.popImmediately())"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(
          blink::mojom::PushEventStatus::PERMISSION_REVOKED_ABUSIVE),
      1);

  //   Missing permission should trigger an automatic unsubscription attempt.
  EXPECT_EQ(app_identifier.app_id(), gcm_driver_->last_deletetoken_app_id());
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));
  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  push_messaging::AppIdentifier app_identifier_afterwards =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin, 0LL);
  EXPECT_TRUE(app_identifier_afterwards.is_null());

  // 1st event - blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED.
  // 2nd event -
  // blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED_ABUSIVE.
  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 2);

  histogram_tester_.ExpectBucketCount(
      "PushMessaging.UnregistrationReason",
      blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED_ABUSIVE, 1);
  histogram_tester_.ExpectBucketCount(
      "PushMessaging.UnregistrationReason",
      blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED, 1);
}

// That test verifies that an origin is not revoked because it is not on
// SafeBrowsing blocking list.
IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTestWithAbusiveOriginPermissionRevocation,
    OriginIsNotOnSafeBrowsingBlockingList) {
  // The origin should be marked as |ABUSIVE_CONTENT| on |CrowdDenyPreloadData|
  // otherwise the permission revocation logic will not be triggered.
  AddToPreloadDataBlocklist(
      https_server()->GetURL("/").DeprecatedGetOriginAsURL(),
      SiteReputation::ABUSIVE_CONTENT);

  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "testdata";
  message.decrypted = true;
  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
  EXPECT_EQ("testdata", RunScript("resultQueue.pop()"));

  // Check that we record this case in UMA.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.DeliveryStatus",
      static_cast<int>(blink::mojom::PushEventStatus::SUCCESS), 1);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestBase,
                       PushEventIgnoresScheduledNotificationsForEnforcement) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  LoadTestPage();  // Reload to become controlled.

  RemoveAllNotifications();

  // We'll need to specify the web_contents in which to eval script, since we're
  // going to run script in a background tab.
  content::WebContents* web_contents =
      GetBrowser()->GetTabStripModel()->GetActiveWebContents();

  // Initialize site engagement score to have no budget for silent pushes.
  SetSiteEngagementScore(web_contents->GetLastCommittedURL(), 0);

  ui_test_utils::NavigateToURLWithDisposition(
      GetBrowser(), GURL("about:blank"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "shownotification-with-showtrigger";
  message.decrypted = true;

  // If the Service Worker push event handler only schedules a notification, we
  // should show a forced one providing there is no foreground tab and the
  // origin ran out of budget.
  SendMessageAndWaitUntilHandled(app_identifier, message);
  EXPECT_EQ("shownotification-with-showtrigger",
            RunScript("resultQueue.pop()", web_contents));

  // Because scheduled notifications do not count as displayed notifications,
  // this should have shown a default notification.
  std::vector<message_center::Notification> notifications =
      GetDisplayedNotifications();
  ASSERT_EQ(notifications.size(), 1u);

  EXPECT_TRUE(TagEquals(notifications[0], kPushMessagingForcedNotificationTag));
  EXPECT_TRUE(notifications[0].silent());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       PushEventEnforcesUserVisibleNotificationAfterQueue) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Fire off two push messages in sequence, only the second one of which will
  // display a notification. The additional round-trip and I/O required by the
  // second message, which shows a notification, should give us a reasonable
  // confidence that the ordering will be maintained.

  std::vector<size_t> number_of_notifications_shown;

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.decrypted = true;

  {
    base::RunLoop run_loop;
    push_service()->SetMessageCallbackForTesting(base::BindRepeating(
        &PushMessagingBrowserTestBase::OnDeliveryFinished,
        base::Unretained(this), &number_of_notifications_shown,
        base::BarrierClosure(2 /* num_closures */, run_loop.QuitClosure())));

    message.raw_data = "testdata";
    push_service()->OnMessage(app_identifier.app_id(), message);

    message.raw_data = "shownotification";
    push_service()->OnMessage(app_identifier.app_id(), message);

    run_loop.Run();
  }

  ASSERT_EQ(2u, number_of_notifications_shown.size());
  EXPECT_EQ(0u, number_of_notifications_shown[0]);
  EXPECT_EQ(1u, number_of_notifications_shown[1]);
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       PushEventNotificationWithoutEventWaitUntil) {
  content::WebContents* web_contents =
      GetBrowser()->GetTabStripModel()->GetActiveWebContents();

  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  base::RunLoop run_loop;
  base::RepeatingClosure quit_barrier =
      base::BarrierClosure(2 /* num_closures */, run_loop.QuitClosure());
  push_service()->SetMessageCallbackForTesting(quit_barrier);
  notification_tester_->SetNotificationAddedClosure(quit_barrier);

  gcm::IncomingMessage message;
  message.sender_id = GetTestApplicationServerKey();
  message.raw_data = "shownotification-without-waituntil";
  message.decrypted = true;
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  push_service()->OnMessage(app_identifier.app_id(), message);
  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(true));
  EXPECT_EQ("immediate:shownotification-without-waituntil",
            RunScript("resultQueue.pop()", web_contents));

  run_loop.Run();

  EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
  ASSERT_EQ(1u, GetNotificationCount());
  EXPECT_TRUE(TagEquals(GetDisplayedNotifications()[0], "push_test_tag"));

  // Verify that the renderer process hasn't crashed.
  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysPrompt) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysGranted) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("documentSubscribePush()").ExtractString()));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysDenied) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());

  EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
            RunScript("documentSubscribePush()"));

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));
}

IN_PROC_BROWSER_TEST_P(PushMessagingPartitionedBrowserTest, CrossOriginFrame) {
  const GURL kEmbedderURL = https_server()->GetURL(
      "embedder.com", "/push_messaging/framed_test.html");
  const GURL kRequesterURL = https_server()->GetURL("requester.com", "/");
  CookieSettingsFactory::GetForProfile(browser()->GetProfile())
      ->SetCookieSetting(kRequesterURL, CONTENT_SETTING_ALLOW);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), kEmbedderURL));

  auto* web_contents = GetBrowser()->GetTabStripModel()->GetActiveWebContents();
  LOG(ERROR) << web_contents->GetLastCommittedURL();
  auto* subframe =
      content::ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0u);
  ASSERT_TRUE(subframe);

  // A cross-origin subframe that had not been granted the NOTIFICATIONS
  // permission previously should see it as "denied", not be able to request it,
  // and not be able to use the Push and Web Notification API. It is verified
  // that no prompts are shown by auto-accepting and still expecting the
  // permission to be denied.

  GetPermissionRequestManager()->set_auto_response_for_test(
      permissions::PermissionRequestManager::ACCEPT_ALL);

  EXPECT_EQ("permission status - denied",
            content::EvalJs(subframe, "requestNotificationPermission();"));

  ASSERT_EQ("ok - service worker registered",
            content::EvalJs(subframe, "registerServiceWorker()"));

  EXPECT_EQ("permission status - denied",
            content::EvalJs(subframe, "pushManagerPermissionState()"));

  EXPECT_EQ("permission status - denied",
            content::EvalJs(subframe, "notificationPermissionState()"));

  EXPECT_EQ("permission status - denied",
            content::EvalJs(subframe, "notificationPermissionAPIState()"));

  EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
            content::EvalJs(subframe, "documentSubscribePush()"));

  // A cross-origin subframe that had been granted the NOTIFICATIONS permission
  // previously (in a first-party context) should see it as "granted", and be
  // able to use the Push and Web Notifications APIs.

  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(kRequesterURL, kRequesterURL,
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_ALLOW);

  GetPermissionRequestManager()->set_auto_response_for_test(
      permissions::PermissionRequestManager::DENY_ALL);

  EXPECT_EQ("permission status - granted",
            content::EvalJs(subframe, "requestNotificationPermission();"));

  EXPECT_EQ("permission status - granted",
            content::EvalJs(subframe, "pushManagerPermissionState()"));

  EXPECT_EQ("permission status - granted",
            content::EvalJs(subframe, "notificationPermissionState()"));

  EXPECT_EQ("permission status - granted",
            content::EvalJs(subframe, "notificationPermissionAPIState()"));

  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      content::EvalJs(subframe, "documentSubscribePush()").ExtractString()));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
  std::string token1;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token1));
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));

  // Resolves true if there was a subscription.
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      1);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());

  // Resolves false if there was no longer a subscription.
  EXPECT_EQ("unsubscribe result: false",
            RunScript("unsubscribeStoredPushSubscription()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      2);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());

  // TODO(johnme): Test that doesn't reject if there was a network error (should
  // deactivate subscription locally anyway).
  // TODO(johnme): Test that doesn't reject if there were other push service
  // errors (should deactivate subscription locally anyway).

  // Unsubscribing (with an existing reference to a PushSubscription), after
  // replacing the Service Worker, actually still works, as the Service Worker
  // registration is unchanged.
  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token2));
  EXPECT_NE(token1, token2);
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
  EXPECT_EQ("ok - service worker replaced",
            RunScript("replaceServiceWorker()"));
  EXPECT_EQ("unsubscribe result: true",
            RunScript("unsubscribeStoredPushSubscription()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      3);

  // Unsubscribing (with an existing reference to a PushSubscription), after
  // unregistering the Service Worker, should fail.
  std::string token3;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token3));
  EXPECT_NE(token1, token3);
  EXPECT_NE(token2, token3);
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));

  // Unregister service worker and wait for callback.
  base::RunLoop run_loop;
  push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
      run_loop.QuitClosure());
  EXPECT_EQ("service worker unregistration status: true",
            RunScript("unregisterServiceWorker()"));
  run_loop.Run();

  // Unregistering should have triggered an automatic unsubscribe.
  histogram_tester_.ExpectBucketCount(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::SERVICE_WORKER_UNREGISTERED),
      1);
  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 4);

  // Now manual unsubscribe should return false.
  EXPECT_EQ("unsubscribe result: false",
            RunScript("unsubscribeStoredPushSubscription()"));
}

// Push subscriptions used to be non-InstanceID GCM registrations. Still need
// to be able to unsubscribe these, even though new ones are no longer created.
// Flaky on some Win and Linux buildbots.  See crbug.com/41384686.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#define MAYBE_LegacyUnsubscribeSuccess DISABLED_LegacyUnsubscribeSuccess
#else
#define MAYBE_LegacyUnsubscribeSuccess LegacyUnsubscribeSuccess
#endif
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       MAYBE_LegacyUnsubscribeSuccess) {
  std::string subscription_id1;
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id1));
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));

  // Resolves true if there was a subscription.
  gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      1);

  // Resolves false if there was no longer a subscription.
  EXPECT_EQ("unsubscribe result: false",
            RunScript("unsubscribeStoredPushSubscription()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      2);

  // Doesn't reject if there was a network error (deactivates subscription
  // locally anyway).
  std::string subscription_id2;
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id2));
  EXPECT_NE(subscription_id1, subscription_id2);
  gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::NETWORK_ERROR);
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      3);
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // Doesn't reject if there were other push service errors (deactivates
  // subscription locally anyway).
  std::string subscription_id3;
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id3));
  EXPECT_NE(subscription_id1, subscription_id3);
  EXPECT_NE(subscription_id2, subscription_id3);
  gcm_service_->AddExpectedUnregisterResponse(
      gcm::GCMClient::INVALID_PARAMETER);
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      4);

  // Unsubscribing (with an existing reference to a PushSubscription), after
  // replacing the Service Worker, actually still works, as the Service Worker
  // registration is unchanged.
  std::string subscription_id4;
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id4));
  EXPECT_NE(subscription_id1, subscription_id4);
  EXPECT_NE(subscription_id2, subscription_id4);
  EXPECT_NE(subscription_id3, subscription_id4);
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));
  EXPECT_EQ("ok - service worker replaced",
            RunScript("replaceServiceWorker()"));
  EXPECT_EQ("unsubscribe result: true",
            RunScript("unsubscribeStoredPushSubscription()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      5);

  // Unsubscribing (with an existing reference to a PushSubscription), after
  // unregistering the Service Worker, should fail.
  std::string subscription_id5;
  ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id5));
  EXPECT_NE(subscription_id1, subscription_id5);
  EXPECT_NE(subscription_id2, subscription_id5);
  EXPECT_NE(subscription_id3, subscription_id5);
  EXPECT_NE(subscription_id4, subscription_id5);
  EXPECT_EQ("ok - stored", RunScript("storePushSubscription()"));

  // Unregister service worker and wait for callback.
  base::RunLoop run_loop;
  push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
      run_loop.QuitClosure());
  EXPECT_EQ("service worker unregistration status: true",
            RunScript("unregisterServiceWorker()"));
  run_loop.Run();

  // Unregistering should have triggered an automatic unsubscribe.
  histogram_tester_.ExpectBucketCount(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::SERVICE_WORKER_UNREGISTERED),
      1);
  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 6);

  // Now manual unsubscribe should return false.
  EXPECT_EQ("unsubscribe result: false",
            RunScript("unsubscribeStoredPushSubscription()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeOffline) {
  EXPECT_NE(push_service(), GetAppHandler());

  std::string token;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token));

  gcm_service_->set_offline(true);

  // Should quickly resolve true after deleting local state (rather than waiting
  // until unsubscribing over the network exceeds the maximum backoff duration).
  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
      1);

  // Since the service is offline, the network request to GCM is still being
  // retried, so the app handler shouldn't have been unregistered yet.
  EXPECT_EQ(push_service(), GetAppHandler());
  // But restarting the push service will unregister the app handler, since the
  // subscription is no longer stored in the push_messaging::AppIdentifier map.
  ASSERT_NO_FATAL_FAILURE(RestartPushService());
  EXPECT_NE(push_service(), GetAppHandler());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       UnregisteringServiceWorkerUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Unregister the worker, and wait for callback to complete.
  base::RunLoop run_loop;
  push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
      run_loop.QuitClosure());
  ASSERT_EQ("service worker unregistration status: true",
            RunScript("unregisterServiceWorker()"));
  run_loop.Run();

  // This should have unregistered the push subscription.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::SERVICE_WORKER_UNREGISTERED),
      1);

  // We should not be able to look up the app id.
  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  push_messaging::AppIdentifier app_identifier =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin,
          0LL /* service_worker_registration_id */);
  EXPECT_TRUE(app_identifier.is_null());

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       ServiceWorkerDatabaseDeletionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Pretend as if the Service Worker database went away, and wait for callback
  // to complete.
  base::RunLoop run_loop;
  push_service()->SetServiceWorkerDatabaseWipedCallbackForTesting(
      run_loop.QuitClosure());
  push_service()->DidDeleteServiceWorkerDatabase();
  run_loop.Run();

  // This should have unregistered the push subscription.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::
                           SERVICE_WORKER_DATABASE_WIPED),
      1);

  // There should not be any subscriptions left.
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       UnregisteringServiceWorkerDeletesUnsubscribedEntries) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());

    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(origin, GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_BLOCK);
    run_loop.Run();
  }

  // There should be no subscription but one unsubscribed entry.
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));

  // Unregister service worker and wait for callback.
  {
    base::RunLoop run_loop;
    push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
        run_loop.QuitClosure());
    EXPECT_EQ("service worker unregistration status: true",
              RunScript("unregisterServiceWorker()"));
    run_loop.Run();
  }

  // There should be no subscription and no unsubscribed entry anymore.
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ServiceWorkerDatabaseDeletionDeletesUnsubscribedEntries) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());

    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(origin, GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_BLOCK);
    run_loop.Run();
  }

  // There should be no subscription but one unsubscribed entry.
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));

  // Pretend as if the Service Worker database went away, and wait for callback
  // to complete.
  {
    base::RunLoop run_loop;
    push_service()->SetServiceWorkerDatabaseWipedCallbackForTesting(
        run_loop.QuitClosure());
    push_service()->DidDeleteServiceWorkerDatabase();
    run_loop.Run();
  }

  // There should be no subscription and no unsubscribed entry anymore.
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       InvalidGetSubscriptionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  push_messaging::AppIdentifier app_identifier1 =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin,
          0LL /* service_worker_registration_id */);
  ASSERT_FALSE(app_identifier1.is_null());

  ASSERT_NO_FATAL_FAILURE(
      DeleteInstanceIDAsIfGCMStoreReset(app_identifier1.app_id()));

  // Push messaging should not yet be aware of the InstanceID being deleted.
  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);
  // We should still be able to look up the app id.
  push_messaging::AppIdentifier app_identifier2 =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin,
          0LL /* service_worker_registration_id */);
  EXPECT_FALSE(app_identifier2.is_null());
  EXPECT_EQ(app_identifier1.app_id(), app_identifier2.app_id());

  // Now call PushManager.getSubscription(). It should return null.
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // This should have unsubscribed the push subscription.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(blink::mojom::PushUnregistrationReason::
                           GET_SUBSCRIPTION_STORAGE_CORRUPT),
      1);
  // We should no longer be able to look up the app id.
  push_messaging::AppIdentifier app_identifier3 =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), origin,
          0LL /* service_worker_registration_id */);
  EXPECT_TRUE(app_identifier3.is_null());

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       GlobalResetPushPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);

  run_loop.Run();

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(
          Property(&PushMessagingUnsubscribedEntry::origin,
                   https_server()->GetURL("/").DeprecatedGetOriginAsURL())));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       LocalResetPushPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, origin,
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_DEFAULT);

  run_loop.Run();

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       DenyPushPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, origin,
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_BLOCK);

  run_loop.Run();

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       GlobalResetNotificationsPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);

  run_loop.Run();

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(
          Property(&PushMessagingUnsubscribedEntry::origin,
                   https_server()->GetURL("/").DeprecatedGetOriginAsURL())));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       LocalResetNotificationsPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, GURL(),
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_DEFAULT);

  run_loop.Run();

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       DenyNotificationsPermissionUnsubscribes) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, GURL(),
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_BLOCK);

  run_loop.Run();

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin, origin)));
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       GrantAlreadyGrantedPermissionDoesNotUnsubscribe) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, GURL(),
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_ALLOW);

  run_loop.Run();

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

// This test is testing some non-trivial content settings rules and make sure
// that they are respected with regards to automatic unsubscription. In other
// words, it checks that the push service does not end up unsubscribing origins
// that have push permission with some non-common rules.
IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
                       AutomaticUnsubscriptionFollowsContentSettingRules) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      base::BarrierClosure(2, run_loop.QuitClosure()));

  GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
                                 CONTENT_SETTING_ALLOW);
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(origin, GURL(),
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_DEFAULT);

  run_loop.Run();

  // The two first rules should give |origin| the permission to use Push even
  // if the rules it used to have have been reset.
  // The Push service should not unsubscribe |origin| because at no point it was
  // left without permission to use Push.

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);

  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

// Checks automatically unsubscribing due to a revoked permission after
// previously clearing site data, under legacy conditions (ie. when
// unregistering a worker did not unsubscribe from push.)
IN_PROC_BROWSER_TEST_F(
    PushMessagingBrowserTest,
    ResetPushPermissionAfterClearingSiteDataUnderLegacyConditions) {
  std::string app_id;
  ASSERT_NO_FATAL_FAILURE(SetupOrphanedPushSubscription(&app_id));

  // Simulate a user clearing site data (including Service Workers, crucially).
  content::BrowsingDataRemover* remover =
      GetBrowser()->GetProfile()->GetBrowsingDataRemover();
  content::BrowsingDataRemoverCompletionObserver observer(remover);
  remover->RemoveAndReply(
      base::Time(), base::Time::Max(),
      chrome_browsing_data_remover::DATA_TYPE_SITE_DATA,
      content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
  observer.BlockUntilCompletion();

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());
  // This shouldn't (asynchronously) cause a DCHECK.
  // TODO(johnme): Get this test running on Android with legacy GCM
  // registrations, which have a different codepath due to sender_id being
  // required for unsubscribing there.
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);

  run_loop.Run();

  // |app_identifier| should no longer be stored in prefs.
  push_messaging::AppIdentifier stored_app_identifier =
      PushMessagingAppIdentifier::FindByAppId(GetBrowser()->GetProfile(),
                                              app_id);
  EXPECT_TRUE(stored_app_identifier.is_null());

  histogram_tester_.ExpectUniqueSample(
      "PushMessaging.UnregistrationReason",
      static_cast<int>(
          blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
      1);

  base::RunLoop().RunUntilIdle();

  // Revoked permission should trigger an automatic unsubscription attempt.
  EXPECT_EQ(app_id, gcm_driver_->last_deletetoken_app_id());
}

IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, EncryptionKeyUniqueness) {
  std::string token1;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token1));

  std::string first_public_key = RunScript("GetP256dh()").ExtractString();
  EXPECT_GE(first_public_key.size(), 32u);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  std::string token2;
  ASSERT_NO_FATAL_FAILURE(
      SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token2));
  EXPECT_NE(token1, token2);

  std::string second_public_key = RunScript("GetP256dh()").ExtractString();
  EXPECT_GE(second_public_key.size(), 32u);

  EXPECT_NE(first_public_key, second_public_key);
}

class PushMessagingIncognitoBrowserTest : public PushMessagingBrowserTestBase {
 public:
  PushMessagingIncognitoBrowserTest()
      : prerender_helper_(base::BindRepeating(
            &PushMessagingIncognitoBrowserTest::web_contents,
            base::Unretained(this))) {}
  ~PushMessagingIncognitoBrowserTest() override = default;

  // PushMessagingBrowserTest:
  void SetUpOnMainThread() override {
    incognito_browser_ = CreateIncognitoBrowser();
    // We SetUp here rather than in SetUp since the https_server isn't yet
    // created at that time.
    prerender_helper_.RegisterServerRequestMonitor(https_server());
    PushMessagingBrowserTestBase::SetUpOnMainThread();
  }
  BrowserWindowInterface* GetBrowser() const override {
    return incognito_browser_;
  }

  content::WebContents* web_contents() {
    return GetBrowser()->GetTabStripModel()->GetActiveWebContents();
  }

 protected:
  content::test::PrerenderTestHelper prerender_helper_;
  raw_ptr<BrowserWindowInterface, AcrossTasksDanglingUntriaged>
      incognito_browser_ = nullptr;
};

// Regression test for https://crbug.com/40413606
IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest,
                       IncognitoGetSubscriptionDoesNotHang) {
  ASSERT_TRUE(GetBrowser()->GetProfile()->IsOffTheRecord());

  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  // In Incognito mode the promise returned by getSubscription should not hang,
  // it should just fulfill with null.
  ASSERT_EQ("false - not subscribed", RunScript("hasSubscription()"));
}

IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest, WarningToCorrectRFH) {
  ASSERT_TRUE(GetBrowser()->GetProfile()->IsOffTheRecord());

  content::WebContentsConsoleObserver console_observer(web_contents());
  console_observer.SetPattern(kIncognitoWarningPattern);

  // Filter out the main frame host of the currently active page.
  console_observer.SetFilter(base::BindLambdaForTesting(
      [&](const content::WebContentsConsoleObserver::Message& message) {
        return message.source_frame->IsInPrimaryMainFrame();
      }));

  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_EQ("AbortError - Registration failed - permission denied",
            RunScript("documentSubscribePush()"));

  ASSERT_TRUE(console_observer.Wait());
  EXPECT_EQ(1u, console_observer.messages().size());
}

// TODO(crbug.com/40204670): This test hits the issue. Re-enable after it
// is fixed.
IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest,
                       DISABLED_WarningToCorrectRFH_Prerender) {
  ASSERT_TRUE(GetBrowser()->GetProfile()->IsOffTheRecord());

  // Load an initial page.
  const GURL initial_url(https_server()->GetURL(GetTestURL()));
  prerender_helper_.NavigatePrimaryPage(initial_url);

  // Register a service worker. This must be done in the primary page as the
  // service worker registration in a prerendered page is deferred until
  // prerender page activation.
  ASSERT_EQ("ok - service worker registered",
            content::EvalJs(web_contents()->GetPrimaryMainFrame(),
                            "registerServiceWorker()"));

  // Start a prerender with the push messaging test URL.
  const GURL prerendering_url(
      https_server()->GetURL(GetTestURL() + "?prerendering"));
  content::PrerenderHostId host_id =
      prerender_helper_.AddPrerender(prerendering_url);
  content::test::PrerenderHostObserver prerender_observer(*web_contents(),
                                                          host_id);
  ASSERT_TRUE(prerender_helper_.GetHostForUrl(prerendering_url));

  content::WebContentsConsoleObserver console_observer(web_contents());
  console_observer.SetPattern(kIncognitoWarningPattern);

  // Filter out the main frame host of the prerendered page.
  content::RenderFrameHost* prerender_rfh =
      prerender_helper_.GetPrerenderedMainFrameHost(host_id);
  console_observer.SetFilter(base::BindLambdaForTesting(
      [&](const content::WebContentsConsoleObserver::Message& message) {
        return message.source_frame == prerender_rfh;
      }));

  // Use ExecuteScriptAsync because binding of blink::mojom::PushMessaging
  // is deferred for the prerendered page. Script execution will finish after
  // the activation.
  ExecuteScriptAsync(prerender_rfh, "documentSubscribePush()");

  // Activate the prerendered page and wait for a response of script execution.
  content::DOMMessageQueue message_queue(web_contents());
  prerender_helper_.NavigatePrimaryPage(prerendering_url);
  // Make sure that the prerender was activated.
  ASSERT_TRUE(prerender_observer.was_activated());
  std::string script_result;
  do {
    ASSERT_TRUE(message_queue.WaitForMessage(&script_result));
  } while (script_result !=
           "\"AbortError - Registration failed - permission denied\"");

  ASSERT_TRUE(console_observer.Wait());
  EXPECT_EQ(1u, console_observer.messages().size());
}

class PushMessagingDisallowSenderIdsBrowserTest
    : public PushMessagingBrowserTestBase {
 public:
  PushMessagingDisallowSenderIdsBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(
        features::kPushMessagingDisallowSenderIDs);
  }

  ~PushMessagingDisallowSenderIdsBrowserTest() override = default;

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

IN_PROC_BROWSER_TEST_F(PushMessagingDisallowSenderIdsBrowserTest,
                       SubscriptionWithSenderIdFails) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Attempt to create a subscription with a GCM Sender ID ("numeric key"),
  // which should fail because the kPushMessagingDisallowSenderIDs feature has
  // been enabled for this test.
  EXPECT_EQ(
      "AbortError - Registration failed - GCM Sender IDs are no longer "
      "supported, please upgrade to VAPID authentication instead",
      RunScript("documentSubscribePushWithNumericKey()"));
}

class PushSubscriptionWithExpirationTimeTest
    : public PushMessagingBrowserTestBase {
 public:
  PushSubscriptionWithExpirationTimeTest() {
    scoped_feature_list_.InitAndEnableFeature(
        features::kPushSubscriptionWithExpirationTime);
  }

  ~PushSubscriptionWithExpirationTimeTest() override = default;

  // Checks whether |expiration_time| lies in the future and is in the
  // valid format (seconds elapsed since Unix time)
  bool IsExpirationTimeValid(const std::string& expiration_time);

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

bool PushSubscriptionWithExpirationTimeTest::IsExpirationTimeValid(
    const std::string& expiration_time) {
  int64_t output;
  if (!base::StringToInt64(expiration_time, &output))
    return false;
  return base::Time::Now().InMillisecondsFSinceUnixEpochIgnoringNull() < output;
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
                       SubscribeGetSubscriptionWithExpirationTime) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // Subscribe with expiration time enabled, should get a subscription with
  // expiration time in the future back
  std::string subscription_expiration_time =
      RunScript("documentSubscribePushGetExpirationTime()").ExtractString();
  EXPECT_TRUE(IsExpirationTimeValid(subscription_expiration_time));

  // Get subscription should also yield a subscription with expiration time
  std::string get_subscription_expiration_time =
      RunScript("GetSubscriptionExpirationTime()").ExtractString();
  EXPECT_TRUE(IsExpirationTimeValid(get_subscription_expiration_time));
  // Both methods should return the same expiration time
  ASSERT_EQ(subscription_expiration_time, get_subscription_expiration_time);
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
                       GetSubscriptionWithExpirationTime) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  // Get subscription should also yield a subscription with expiration time
  EXPECT_TRUE(IsExpirationTimeValid(
      RunScript("GetSubscriptionExpirationTime()").ExtractString()));
}

class PushSubscriptionWithoutExpirationTimeTest
    : public PushMessagingBrowserTestBase {
 public:
  PushSubscriptionWithoutExpirationTimeTest() {
    // Override current feature list to ensure having
    // |kPushSubscriptionWithExpirationTime| disabled
    scoped_feature_list_.InitAndDisableFeature(
        features::kPushSubscriptionWithExpirationTime);
  }

  ~PushSubscriptionWithoutExpirationTimeTest() override = default;

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

IN_PROC_BROWSER_TEST_F(PushSubscriptionWithoutExpirationTimeTest,
                       SubscribeDocumentExpirationTimeNull) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));

  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());

  LoadTestPage();  // Reload to become controlled.

  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  // When |features::kPushSubscriptionWithExpirationTime| is disabled,
  // expiration time should be null
  EXPECT_EQ("null", RunScript("documentSubscribePushGetExpirationTime()"));
}

class PushSubscriptionChangeEventOnInvalidationTest
    : public PushMessagingBrowserTestBase {
 public:
  PushSubscriptionChangeEventOnInvalidationTest() {
    scoped_feature_list_.InitWithFeatures(
        {features::kPushSubscriptionChangeEventOnInvalidation,
         features::kPushSubscriptionWithExpirationTime},
        {});
  }

  ~PushSubscriptionChangeEventOnInvalidationTest() override = default;

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

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnInvalidationTest,
                       PushSubscriptionChangeEventSuccess) {
  // Create the |old_subscription| by subscribing and unsubscribing again
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  blink::mojom::PushSubscriptionPtr old_subscription =
      GetSubscriptionForAppIdentifier(app_identifier);

  EXPECT_EQ("unsubscribe result: true", RunScript("unsubscribePush()"));

  // There should be no subscription since we unsubscribed
  EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->GetProfile()),
            0u);

  // Create a |new_subscription| by resubscribing
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
  app_identifier = GetAppIdentifierForServiceWorkerRegistration(0LL);

  blink::mojom::PushSubscriptionPtr new_subscription =
      GetSubscriptionForAppIdentifier(app_identifier);

  // Save the endpoints to compare with the JS result
  GURL old_endpoint = old_subscription->endpoint;
  GURL new_endpoint = new_subscription->endpoint;

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  base::RunLoop run_loop;
  push_service()->FirePushSubscriptionChangeForAppIdentifier(
      app_identifier, run_loop.QuitClosure(), std::move(new_subscription),
      std::move(old_subscription));
  run_loop.Run();

  // Compare old subscription
  EXPECT_EQ(old_endpoint.spec(), RunScript("resultQueue.pop()"));
  // Compare new subscription
  EXPECT_EQ(new_endpoint.spec(), RunScript("resultQueue.pop()"));
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnInvalidationTest,
                       FiredAfterPermissionRevoked) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);
  auto old_subscription = GetSubscriptionForAppIdentifier(app_identifier);

  base::RunLoop run_loop;
  push_service()->SetContentSettingChangedCallbackForTesting(
      run_loop.QuitClosure());
  HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
      ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                      ContentSettingsType::NOTIFICATIONS,
                                      CONTENT_SETTING_BLOCK);
  run_loop.Run();

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));

  // Check if the pushsubscriptionchangeevent arrived in the document and
  // whether the |old_subscription| has the expected endpoint and
  // |new_subscription| is null
  EXPECT_EQ(old_subscription->endpoint.spec(), RunScript("resultQueue.pop()"));
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnInvalidationTest,
                       OnInvalidation) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);
  ASSERT_FALSE(app_identifier.is_null());

  base::RunLoop run_loop;
  push_service()->SetInvalidationCallbackForTesting(run_loop.QuitClosure());
  push_service()->OnSubscriptionInvalidation(app_identifier.app_id());
  run_loop.Run();

  // Old subscription should be gone
  push_messaging::AppIdentifier deleted_identifier =
      PushMessagingAppIdentifier::FindByAppId(GetBrowser()->GetProfile(),
                                              app_identifier.app_id());
  EXPECT_TRUE(deleted_identifier.is_null());

  // New subscription with a different app id should exist
  push_messaging::AppIdentifier new_identifier =
      PushMessagingAppIdentifier::FindByServiceWorker(
          GetBrowser()->GetProfile(), app_identifier.origin(),
          app_identifier.service_worker_registration_id());
  EXPECT_FALSE(new_identifier.is_null());

  base::RunLoop().RunUntilIdle();

  // Expect `pushsubscriptionchange` event that is not null
  EXPECT_NE("null", RunScript("resultQueue.pop()"));
  EXPECT_NE("null", RunScript("resultQueue.pop()"));
}

using PushSubscriptionChangeEventOnResubscribeTest =
    PushMessagingBrowserTestBase;

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnResubscribeTest,
                       FiredAfterPermissionBlockedAndRegranted) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_BLOCK);
    run_loop.Run();
  }

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 1,
                                      1);
  histogram_tester_.ExpectBucketCount(
      "PushMessaging."
      "PushSubscriptionChangeForNotificationPermissionChangeFired",
      1, 1);

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  // Check if the `pushsubscriptionchange` event arrived in the service worker.
  // Upon firing the `pushsubscriptionchange` event, the service worker will
  // respond to the test runner with the old and new endpoints. Both will be
  // NULL when the event fires after permission has been reset, as the old
  // subscription had already been deleted and no new subscription is created
  // automatically.
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));

  // The unsubscribed entry should not be deleted yet, since the subscription
  // has not been recreated.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin,
                           app_identifier.origin())));
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // Now resubscribe from the worker.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
                      true /* standard_protocol */));
  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  // Now the unsubscribed entry should have been deleted.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnResubscribeTest,
                       FiredAfterPermissionResetAndRegranted) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_ASK);
    run_loop.Run();
  }

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 1,
                                      1);
  histogram_tester_.ExpectBucketCount(
      "PushMessaging."
      "PushSubscriptionChangeForNotificationPermissionChangeFired",
      1, 1);

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  // Check if the `pushsubscriptionchange` event arrived in the service worker.
  // Upon firing the `pushsubscriptionchange` event, the service worker will
  // respond to the test runner with the old and new endpoints. Both will be
  // NULL when the event fires after permission has been reset, as the old
  // subscription had already been deleted and no new subscription is created
  // automatically.
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));

  // The unsubscribed entry should not be deleted yet, since the subscription
  // has not been recreated.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin,
                           app_identifier.origin())));
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // Now resubscribe from the worker.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
                      true /* standard_protocol */));
  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  // Now the unsubscribed entry should have been deleted.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnResubscribeTest,
                       FiredAfterGlobalPermissionResetAndRegranted) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);

    run_loop.Run();
  }

  EXPECT_EQ("permission status - prompt",
            RunScript("pushManagerPermissionState()"));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 1,
                                      1);
  histogram_tester_.ExpectBucketCount(
      "PushMessaging."
      "PushSubscriptionChangeForNotificationPermissionChangeFired",
      1, 1);

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  // Check if the `pushsubscriptionchange` event arrived in the service worker.
  // Upon firing the `pushsubscriptionchange` event, the service worker will
  // respond to the test runner with the old and new endpoints. Both will be
  // NULL when the event fires after permission has been reset, as the old
  // subscription had already been deleted and no new subscription is created
  // automatically.
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));

  // The unsubscribed entry should not be deleted yet, since the subscription
  // has not been recreated.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin,
                           app_identifier.origin())));
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // Now resubscribe from the worker.
  ASSERT_NO_FATAL_FAILURE(
      EndpointToToken(RunScript("workerSubscribePush()").ExtractString(),
                      true /* standard_protocol */));
  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  // Now the unsubscribed entry should have been deleted.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());
}

IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventOnResubscribeTest,
                       NotFiredForWildcardContentSettingRegranted) {
  ASSERT_EQ("ok - service worker registered",
            RunScript("registerServiceWorker()"));
  ASSERT_EQ("manifest removed", RunScript("removeManifest()"));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingCustomScope(ContentSettingsPattern::Wildcard(),
                                       ContentSettingsPattern::Wildcard(),
                                       ContentSettingsType::NOTIFICATIONS,
                                       CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  ASSERT_NO_FATAL_FAILURE(EndpointToToken(
      RunScript("documentSubscribePush()").ExtractString(), true, nullptr));

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingCustomScope(ContentSettingsPattern::Wildcard(),
                                       ContentSettingsPattern::Wildcard(),
                                       ContentSettingsType::NOTIFICATIONS,
                                       CONTENT_SETTING_BLOCK);
    run_loop.Run();
  }

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));
  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // There should be one unsubscribed entry.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin,
                           app_identifier.origin())));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingCustomScope(ContentSettingsPattern::Wildcard(),
                                       ContentSettingsPattern::Wildcard(),
                                       ContentSettingsType::NOTIFICATIONS,
                                       CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  // The unsubscribed entry should not be deleted.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      ElementsAre(Property(&PushMessagingUnsubscribedEntry::origin,
                           app_identifier.origin())));

  EXPECT_EQ("false - not subscribed", RunScript("hasSubscription()"));

  // The `pushsubscriptionchange` event should not have been fired. Since it's
  // difficult to deterministically check that that didn't happen, we instead
  // rely on UMA metrics.
  histogram_tester_.ExpectUniqueSample(
      "PushMessaging."
      "PushSubscriptionChangeForNotificationPermissionChangeFired",
      0, 3);
  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 1,
                                      0);
  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 0,
                                      3);
}

class PushSubscriptionChangeEventOnResubscribeWithAutoResubscribeTest
    : public PushSubscriptionChangeEventOnResubscribeTest {
 protected:
  std::string GetTestURL() override {
    return PushSubscriptionChangeEventOnResubscribeTest::GetTestURL() +
           "?autoResubscribe=true";
  }
};

IN_PROC_BROWSER_TEST_F(
    PushSubscriptionChangeEventOnResubscribeWithAutoResubscribeTest,
    ServiceWorkersResubscribesSuccessfullyAfterRegrant) {
  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  ASSERT_EQ("false - is not controlled", RunScript("isControlled()"));
  LoadTestPage();  // Reload to become controlled.
  ASSERT_EQ("true - is controlled", RunScript("isControlled()"));

  push_messaging::AppIdentifier app_identifier =
      GetAppIdentifierForServiceWorkerRegistration(0LL);

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_BLOCK);
    run_loop.Run();
  }

  EXPECT_EQ("permission status - denied",
            RunScript("pushManagerPermissionState()"));

  {
    base::RunLoop run_loop;
    push_service()->SetContentSettingChangedCallbackForTesting(
        run_loop.QuitClosure());
    HostContentSettingsMapFactory::GetForProfile(GetBrowser()->GetProfile())
        ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
                                        ContentSettingsType::NOTIFICATIONS,
                                        CONTENT_SETTING_ALLOW);
    run_loop.Run();
  }

  histogram_tester_.ExpectBucketCount("PushMessaging.NumUnsubscribedEntries", 1,
                                      1);
  histogram_tester_.ExpectBucketCount(
      "PushMessaging."
      "PushSubscriptionChangeForNotificationPermissionChangeFired",
      1, 1);

  EXPECT_EQ("permission status - granted",
            RunScript("pushManagerPermissionState()"));

  // Check if the `pushsubscriptionchange` event arrived in the service worker.
  // Upon firing the `pushsubscriptionchange` event, the service worker will
  // respond to the test runner with the old and new endpoints. Both will be
  // NULL when the event fires after permission has been reset, as the old
  // subscription had already been deleted and no new subscription is created
  // automatically. Afterwards, the service worker will immediately try to
  // resubscribe and send to the test runner the new endpoint.
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
  EXPECT_EQ("null", RunScript("resultQueue.pop()"));
  auto got_new_endpoint = RunScript("resultQueue.pop()");

  blink::mojom::PushSubscriptionPtr new_subscription =
      GetSubscriptionForAppIdentifier(app_identifier);
  EXPECT_EQ(new_subscription->endpoint, got_new_endpoint);

  // Now the unsubscribed entry should have been deleted.
  EXPECT_THAT(
      PushMessagingUnsubscribedEntry::GetAll(GetBrowser()->GetProfile()),
      IsEmpty());

  EXPECT_EQ("true - subscribed", RunScript("hasSubscription()"));
}
