summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc')
-rw-r--r--chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc3227
1 files changed, 3227 insertions, 0 deletions
diff --git a/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
new file mode 100644
index 00000000000..b94212aad9f
--- /dev/null
+++ b/chromium/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -0,0 +1,3227 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// 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/bind.h"
+#include "base/command_line.h"
+#include "base/memory/raw_ptr.h"
+#include "base/run_loop.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/chrome_notification_types.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_constants.h"
+#include "chrome/browser/push_messaging/push_messaging_features.h"
+#include "chrome/browser/push_messaging/push_messaging_service_factory.h"
+#include "chrome/browser/push_messaging/push_messaging_service_impl.h"
+#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/buildflags.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/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/network_session_configurator/common/network_switches.h"
+#include "components/permissions/permission_request_manager.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/notification_service.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 "content/public/test/test_utils.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"
+
+#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
+#include "chrome/browser/background/background_mode_manager.h"
+#endif
+
+namespace {
+
+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/401439). 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(kApplicationServerKey,
+ kApplicationServerKey + base::size(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 absl::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:
+ 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());
+ 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);
+
+ // HTTPS server only serves a valid cert for localhost, so this is needed to
+ // load webby domains like "embedded.com" without an interstitial.
+ command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
+ }
+
+ // InProcessBrowserTest:
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(https_server_->Start());
+
+ KeyedService* keyed_service =
+ gcm::GCMProfileServiceFactory::GetForProfile(GetBrowser()->profile());
+ 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()->profile());
+
+ push_service_ =
+ PushMessagingServiceFactory::GetForProfile(GetBrowser()->profile());
+
+ 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()->profile();
+ 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()); }
+
+ bool RunScript(const std::string& script, std::string* result) {
+ return RunScript(script, result, nullptr);
+ }
+
+ bool RunScript(const std::string& script, std::string* result,
+ content::WebContents* web_contents) {
+ if (!web_contents)
+ web_contents = GetBrowser()->tab_strip_model()->GetActiveWebContents();
+ return content::ExecuteScriptAndExtractString(web_contents->GetMainFrame(),
+ script, result);
+ }
+
+ gcm::GCMAppHandler* GetAppHandler() {
+ return gcm_driver_->GetAppHandler(kPushMessagingAppIdentifierPrefix);
+ }
+
+ permissions::PermissionRequestManager* GetPermissionRequestManager() {
+ return permissions::PermissionRequestManager::FromWebContents(
+ GetBrowser()->tab_strip_model()->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 PushMessagingAppIdentifier& 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 PushMessagingAppIdentifier map and Service Worker DB.
+ // Calls should be wrapped in the ASSERT_NO_FATAL_FAILURE() macro.
+ void DeleteInstanceIDAsIfGCMStoreReset(const std::string& app_id);
+
+ PushMessagingAppIdentifier GetAppIdentifierForServiceWorkerRegistration(
+ int64_t service_worker_registration_id);
+
+ void SendMessageAndWaitUntilHandled(
+ const PushMessagingAppIdentifier& 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()->profile());
+ 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 Browser* GetBrowser() const { return browser(); }
+
+ gcm::GCMProfileServiceFactory::ScopedTestingFactoryInstaller
+ scoped_testing_factory_installer_;
+
+ raw_ptr<gcm::FakeGCMProfileService> gcm_service_;
+ raw_ptr<instance_id::FakeGCMDriverForInstanceID> gcm_driver_;
+ base::HistogramTester histogram_tester_;
+
+ std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
+
+ private:
+ std::unique_ptr<net::EmbeddedTestServer> https_server_;
+ raw_ptr<PushMessagingServiceImpl> push_service_;
+};
+
+void PushMessagingBrowserTestBase::RequestAndAcceptPermission() {
+ std::string script_result;
+ GetPermissionRequestManager()->set_auto_response_for_test(
+ permissions::PermissionRequestManager::ACCEPT_ALL);
+ ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
+ ASSERT_EQ("permission status - granted", script_result);
+}
+
+void PushMessagingBrowserTestBase::RequestAndDenyPermission() {
+ std::string script_result;
+ GetPermissionRequestManager()->set_auto_response_for_test(
+ permissions::PermissionRequestManager::DENY_ALL);
+ ASSERT_TRUE(RunScript("requestNotificationPermission();", &script_result));
+ ASSERT_EQ("permission status - denied", script_result);
+}
+
+void PushMessagingBrowserTestBase::SubscribeSuccessfully(
+ PushSubscriptionKeyFormat key_format,
+ std::string* out_token) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ switch (key_format) {
+ case PushSubscriptionKeyFormat::kBinary:
+ ASSERT_TRUE(RunScript("removeManifest()", &script_result));
+ ASSERT_EQ("manifest removed", script_result);
+
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, true, out_token));
+ break;
+ case PushSubscriptionKeyFormat::kBase64UrlEncoded:
+ ASSERT_TRUE(RunScript("removeManifest()", &script_result));
+ ASSERT_EQ("manifest removed", script_result);
+
+ ASSERT_TRUE(RunScript("documentSubscribePushWithBase64URLEncodedString()",
+ &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, true, out_token));
+ break;
+ case PushSubscriptionKeyFormat::kOmitKey:
+ // Test backwards compatibility with old ID based subscriptions.
+ ASSERT_TRUE(
+ RunScript("documentSubscribePushWithoutKey()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result, 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, std::move(options),
+ base::BindOnce(&DidRegister, run_loop.QuitClosure()));
+ run_loop.Run();
+
+ PushMessagingAppIdentifier app_identifier =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), 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.
+
+ std::string script_result;
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ GURL requesting_origin =
+ https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ int64_t service_worker_registration_id = 0LL;
+ PushMessagingAppIdentifier app_identifier =
+ PushMessagingAppIdentifier::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);
+ }
+
+ app_identifier.PersistToPrefs(GetBrowser()->profile());
+ push_service_->IncreasePushSubscriptionCount(1, false /* is_pending */);
+ push_service_->DecreasePushSubscriptionCount(1, true /* was_pending */);
+
+ {
+ base::RunLoop run_loop;
+ push_service_->StorePushSubscriptionForTesting(
+ GetBrowser()->profile(), 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_EQ(kPushMessagingGcmEndpoint, endpoint.substr(0, last_slash + 1));
+
+ ASSERT_LT(last_slash + 1, endpoint.length()); // Token must not be empty.
+
+ if (out_token)
+ *out_token = endpoint.substr(last_slash + 1);
+}
+
+PushMessagingAppIdentifier
+PushMessagingBrowserTestBase::GetAppIdentifierForServiceWorkerRegistration(
+ int64_t service_worker_registration_id) {
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ PushMessagingAppIdentifier app_identifier =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), 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 PushMessagingAppIdentifier 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()->profile());
+ 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 PushMessagingAppIdentifier& 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() {
+ feature_list_.InitAndDisableFeature(
+ features::kPushMessagingDisallowSenderIDs);
+ }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+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) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ GetPermissionRequestManager()->set_auto_response_for_test(
+ permissions::PermissionRequestManager::ACCEPT_ALL);
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ // Both of these methods EXPECT that they succeed.
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ SubscribeFailureNotificationsBlocked) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
+
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
+ script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoManifest) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ ASSERT_TRUE(RunScript("removeManifest()", &script_result));
+ ASSERT_EQ("manifest removed", script_result);
+
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, and "
+ "manifest empty or missing",
+ script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribeFailureNoSenderId) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ ASSERT_TRUE(RunScript("swapManifestNoSenderId()", &script_result));
+ ASSERT_EQ("sender id removed from manifest", script_result);
+
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, and "
+ "gcm_sender_id not found in manifest",
+ script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ RegisterFailureEmptyPushSubscriptionOptions) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ ASSERT_TRUE(
+ RunScript("documentSubscribePushWithEmptyOptions()", &script_result));
+ EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
+ script_result);
+}
+
+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);
+
+ PushMessagingAppIdentifier app_identifier =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(),
+ 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) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Try to subscribe from a worker without a key. This should fail.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, and "
+ "gcm_sender_id not found in manifest",
+ script_result);
+
+ // Now run the subscribe with a key. This should succeed.
+ ASSERT_TRUE(RunScript("workerSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, true /* standard_protocol */));
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ SubscribeWorkerWithBase64URLEncodedString) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Try to subscribe from a worker without a key. This should fail.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, and "
+ "gcm_sender_id not found in manifest",
+ script_result);
+
+ // Now run the subscribe with a key. This should succeed.
+ ASSERT_TRUE(RunScript("workerSubscribePushWithBase64URLEncodedString()",
+ &script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, true /* standard_protocol */));
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ ResubscribeWithoutKeyAfterSubscribingWithKeyInManifest) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ std::string token1;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token1));
+
+ ASSERT_TRUE(RunScript("removeManifest()", &script_result));
+ ASSERT_EQ("manifest removed", script_result);
+
+ // Try to resubscribe from the document without a key or manifest.
+ // This should fail.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and manifest empty or missing",
+ script_result);
+
+ // Now run the subscribe from the service worker without a key.
+ // In this case, the sender id should be read from the datastore.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token2;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EXPECT_EQ(token1, token2);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token3;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EXPECT_NE(token1, token3);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(
+ PushMessagingBrowserTest,
+ ResubscribeWithoutKeyAfterSubscribingFromDocumentWithP256Key) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPageWithoutManifest(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Run the subscription from the document with a key.
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+
+ // Try to resubscribe from the document without a key - should fail.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and manifest empty or missing",
+ script_result);
+
+ // Now try to resubscribe from the service worker without a key.
+ // This should also fail as the original key was not numeric.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and gcm_sender_id not found in manifest",
+ script_result);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // After unsubscribing, try to resubscribe again without a key.
+ // This should again fail.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and gcm_sender_id not found in manifest",
+ script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(
+ PushMessagingBrowserTest,
+ ResubscribeWithoutKeyAfterSubscribingFromWorkerWithP256Key) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPageWithoutManifest(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Run the subscribe from the service worker with a key.
+ // This should succeed.
+ ASSERT_TRUE(RunScript("workerSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, true /* standard_protocol */));
+
+ // Try to resubscribe from the document without a key - should fail.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and manifest empty or missing",
+ script_result);
+
+ // Now try to resubscribe from the service worker without a key.
+ // This should also fail as the original key was not numeric.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, and "
+ "gcm_sender_id not found in manifest",
+ script_result);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // After unsubscribing, try to resubscribe again without a key.
+ // This should again fail.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and gcm_sender_id not found in manifest",
+ script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(
+ PushMessagingBrowserTest,
+ ResubscribeWithoutKeyAfterSubscribingFromDocumentWithNumber) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPageWithoutManifest(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Run the subscribe from the document with a numeric key.
+ // This should succeed.
+ ASSERT_TRUE(
+ RunScript("documentSubscribePushWithNumericKey()", &script_result));
+ std::string token1;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token1));
+
+ // Try to resubscribe from the document without a key - should fail.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and manifest empty or missing",
+ script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token2;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EXPECT_EQ(token1, token2);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token3;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EXPECT_NE(token1, token3);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(
+ PushMessagingBrowserTest,
+ ResubscribeWithoutKeyAfterSubscribingFromWorkerWithNumber) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPageWithoutManifest(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Run the subscribe from the service worker with a numeric key.
+ // This should succeed.
+ ASSERT_TRUE(RunScript("workerSubscribePushWithNumericKey()", &script_result));
+ std::string token1;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token1));
+
+ // Try to resubscribe from the document without a key - should fail.
+ ASSERT_TRUE(RunScript("documentSubscribePushWithoutKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - missing applicationServerKey, "
+ "and manifest empty or missing",
+ script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token2;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EXPECT_EQ(token1, token2);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("workerSubscribePushNoKey()", &script_result));
+ std::string token3;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EXPECT_NE(token1, token3);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, ResubscribeWithMismatchedKey) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Run the subscribe from the service worker with a key.
+ // This should succeed.
+ ASSERT_TRUE(
+ RunScript("workerSubscribePushWithNumericKey('11111')", &script_result));
+ std::string token1;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token1));
+
+ // Try to resubscribe with a different key - should fail.
+ ASSERT_TRUE(
+ RunScript("workerSubscribePushWithNumericKey('22222')", &script_result));
+ EXPECT_EQ(
+ "InvalidStateError - Registration failed - A subscription with a "
+ "different applicationServerKey (or gcm_sender_id) already exists; to "
+ "change the applicationServerKey, unsubscribe then resubscribe.",
+ script_result);
+
+ // Try to resubscribe with the original key - should succeed.
+ ASSERT_TRUE(
+ RunScript("workerSubscribePushWithNumericKey('11111')", &script_result));
+ std::string token2;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token2));
+ EXPECT_EQ(token1, token2);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // Resubscribe with a different key after unsubscribing.
+ // Should succeed, and we should get a new subscription token.
+ ASSERT_TRUE(
+ RunScript("workerSubscribePushWithNumericKey('22222')", &script_result));
+ std::string token3;
+ ASSERT_NO_FATAL_FAILURE(
+ EndpointToToken(script_result, false /* standard_protocol */, &token3));
+ EXPECT_NE(token1, token3);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, SubscribePersisted) {
+ std::string script_result;
+
+ // 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));
+ PushMessagingAppIdentifier sw0_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+ EXPECT_EQ(sw0_identifier.app_id(), gcm_driver_->last_gettoken_app_id());
+
+ LoadTestPage("/push_messaging/subscope1/test.html");
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ LoadTestPage("/push_messaging/subscope2/test.html");
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ // 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);
+ PushMessagingAppIdentifier 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);
+ PushMessagingAppIdentifier 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());
+
+ std::string script_result;
+
+ // Unsubscribe.
+ base::RunLoop run_loop;
+ push_service()->SetUnsubscribeCallbackForTesting(run_loop.QuitClosure());
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ // 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ 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));
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("testdata", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus.FindServiceWorker",
+ 0 /* SERVICE_WORKER_OK */, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent",
+ 0 /* SERVICE_WORKER_OK */, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus",
+ static_cast<int>(blink::mojom::PushEventStatus::SUCCESS), 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventOnShutdown) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
+ gcm::IncomingMessage message;
+ message.sender_id = GetTestApplicationServerKey();
+ message.raw_data = "testdata";
+ message.decrypted = true;
+ push_service()->Observe(chrome::NOTIFICATION_APP_TERMINATING,
+ content::NotificationService::AllSources(),
+ content::NotificationService::NoDetails());
+ push_service()->OnMessage(app_identifier.app_id(), message);
+ EXPECT_TRUE(IsRegisteredKeepAliveEqualTo(false));
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPayload) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ gcm::IncomingMessage message;
+ message.sender_id = GetTestApplicationServerKey();
+ message.decrypted = false;
+
+ push_service()->OnMessage(app_identifier.app_id(), message);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("[NULL]", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, LegacyPushEvent) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ gcm::IncomingMessage message;
+ message.sender_id = kManifestSenderId;
+ message.decrypted = false;
+
+ push_service()->OnMessage(app_identifier.app_id(), message);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("[NULL]", script_result);
+}
+
+// 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.
+ std::string script_result;
+ ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
+ EXPECT_EQ("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus.FindServiceWorker",
+ 5 /* SERVICE_WORKER_ERROR_NOT_FOUND */, 1);
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent", 0);
+ 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.
+ PushMessagingAppIdentifier stored_app_identifier =
+ PushMessagingAppIdentifier::FindByAppId(GetBrowser()->profile(), 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ 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.
+ ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
+ EXPECT_EQ("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.FindServiceWorker", 0);
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent", 0);
+ 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/633310).
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PushEventWithoutPermission) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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()->profile())
+ ->RemoveObserver(push_service());
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->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.
+ ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
+ EXPECT_EQ("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.FindServiceWorker", 0);
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent", 0);
+ 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());
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ PushMessagingAppIdentifier app_identifier_afterwards =
+ PushMessagingAppIdentifier::FindByServiceWorker(GetBrowser()->profile(),
+ origin, 0LL);
+ EXPECT_TRUE(app_identifier_afterwards.is_null());
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::DELIVERY_PERMISSION_DENIED),
+ 1);
+}
+
+// https://crbug.com/458160 test is flaky on all platforms; but mostly linux.
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ DISABLED_PushEventEnforcesUserVisibleNotification) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ 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()->tab_strip_model()->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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("testdata", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("shownotification", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("testdata", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("testdata", script_result);
+
+ {
+ 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.
+ message.raw_data = "shownotification";
+ SendMessageAndWaitUntilHandled(app_identifier, message);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("shownotification", script_result);
+
+ {
+ 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier 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_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ 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()->tab_strip_model()->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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("testdata", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("testdata", script_result);
+
+ // 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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("testdata", script_result);
+
+ 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) {
+ safe_browsing::ThreatMetadata test_metadata;
+ test_metadata.api_permissions.emplace("NOTIFICATIONS");
+ fake_database_manager_->SetSimulatedMetadataForUrl(url, test_metadata);
+ }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+ absl::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());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ LoadTestPage(); // Reload to become controlled.
+ std::string script_result;
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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.
+ ASSERT_TRUE(RunScript("resultQueue.popImmediately()", &script_result));
+ EXPECT_EQ("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.FindServiceWorker", 0);
+ histogram_tester_.ExpectTotalCount(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent", 0);
+ 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());
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ PushMessagingAppIdentifier app_identifier_afterwards =
+ PushMessagingAppIdentifier::FindByServiceWorker(GetBrowser()->profile(),
+ 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) {
+ std::string script_result;
+
+ // 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());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ 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));
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("testdata", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus.FindServiceWorker",
+ 0 /* SERVICE_WORKER_OK */, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus.ServiceWorkerEvent",
+ 0 /* SERVICE_WORKER_OK */, 1);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.DeliveryStatus",
+ static_cast<int>(blink::mojom::PushEventStatus::SUCCESS), 1);
+}
+
+class PushMessagingBrowserTestWithNotificationTriggersEnabled
+ : public PushMessagingBrowserTestBase {
+ public:
+ PushMessagingBrowserTestWithNotificationTriggersEnabled() {
+ feature_list_.InitAndEnableFeature(features::kNotificationTriggers);
+ }
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTestWithNotificationTriggersEnabled,
+ PushEventIgnoresScheduledNotificationsForEnforcement) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier 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()->tab_strip_model()->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);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("shownotification-with-showtrigger", script_result);
+
+ // 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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) {
+ std::string script_result;
+ content::WebContents* web_contents =
+ GetBrowser()->tab_strip_model()->GetActiveWebContents();
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ 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));
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result, web_contents));
+ EXPECT_EQ("immediate:shownotification-without-waituntil", script_result);
+
+ 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.
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysPrompt) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ ASSERT_EQ("permission status - prompt", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysGranted) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, PermissionStateSaysDenied) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndDenyPermission());
+
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
+ script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, CrossOriginFrame) {
+ const GURL kEmbedderURL = https_server()->GetURL(
+ "embedder.com", "/push_messaging/framed_test.html");
+ const GURL kRequesterURL = https_server()->GetURL("requester.com", "/");
+
+ ASSERT_TRUE(ui_test_utils::NavigateToURL(GetBrowser(), kEmbedderURL));
+
+ auto* web_contents = GetBrowser()->tab_strip_model()->GetActiveWebContents();
+ LOG(ERROR) << web_contents->GetLastCommittedURL();
+ auto* subframe = content::ChildFrameAt(web_contents->GetMainFrame(), 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);
+
+ std::string script_result;
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "requestNotificationPermission();", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "notificationPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "notificationPermissionAPIState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "documentSubscribePush()", &script_result));
+ EXPECT_EQ("NotAllowedError - Registration failed - permission denied",
+ script_result);
+
+ // 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()->profile())
+ ->SetContentSettingDefaultScope(kRequesterURL, kRequesterURL,
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_ALLOW);
+
+ GetPermissionRequestManager()->set_auto_response_for_test(
+ permissions::PermissionRequestManager::DENY_ALL);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "requestNotificationPermission();", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "notificationPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "notificationPermissionAPIState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ subframe, "documentSubscribePush()", &script_result));
+ ASSERT_NO_FATAL_FAILURE(EndpointToToken(script_result));
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeSuccess) {
+ std::string script_result;
+
+ std::string token1;
+ ASSERT_NO_FATAL_FAILURE(
+ SubscribeSuccessfully(PushSubscriptionKeyFormat::kOmitKey, &token1));
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+
+ // Resolves true if there was a subscription.
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
+ 1);
+
+ // Resolves false if there was no longer a subscription.
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: false", script_result);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
+ 2);
+
+ // 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);
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+ ASSERT_TRUE(RunScript("replaceServiceWorker()", &script_result));
+ EXPECT_EQ("ok - service worker replaced", script_result);
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+
+ // Unregister service worker and wait for callback.
+ base::RunLoop run_loop;
+ push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
+ run_loop.QuitClosure());
+ ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
+ EXPECT_EQ("service worker unregistration status: true", script_result);
+ 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.
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: false", script_result);
+}
+
+// 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/835382.
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#define MAYBE_LegacyUnsubscribeSuccess DISABLED_LegacyUnsubscribeSuccess
+#else
+#define MAYBE_LegacyUnsubscribeSuccess LegacyUnsubscribeSuccess
+#endif
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ MAYBE_LegacyUnsubscribeSuccess) {
+ std::string script_result;
+
+ std::string subscription_id1;
+ ASSERT_NO_FATAL_FAILURE(LegacySubscribeSuccessfully(&subscription_id1));
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+
+ // Resolves true if there was a subscription.
+ gcm_service_->AddExpectedUnregisterResponse(gcm::GCMClient::SUCCESS);
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
+ 1);
+
+ // Resolves false if there was no longer a subscription.
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: false", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(blink::mojom::PushUnregistrationReason::JAVASCRIPT_API),
+ 3);
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ // 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);
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+ ASSERT_TRUE(RunScript("replaceServiceWorker()", &script_result));
+ EXPECT_EQ("ok - service worker replaced", script_result);
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ 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);
+ ASSERT_TRUE(RunScript("storePushSubscription()", &script_result));
+ EXPECT_EQ("ok - stored", script_result);
+
+ // Unregister service worker and wait for callback.
+ base::RunLoop run_loop;
+ push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
+ run_loop.QuitClosure());
+ ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
+ EXPECT_EQ("service worker unregistration status: true", script_result);
+ 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.
+ ASSERT_TRUE(RunScript("unsubscribeStoredPushSubscription()", &script_result));
+ EXPECT_EQ("unsubscribe result: false", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest, UnsubscribeOffline) {
+ std::string script_result;
+
+ 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).
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+ 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 PushMessagingAppIdentifier map.
+ ASSERT_NO_FATAL_FAILURE(RestartPushService());
+ EXPECT_NE(push_service(), GetAppHandler());
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ UnregisteringServiceWorkerUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Unregister the worker, and wait for callback to complete.
+ base::RunLoop run_loop;
+ push_service()->SetServiceWorkerUnregisteredCallbackForTesting(
+ run_loop.QuitClosure());
+ ASSERT_TRUE(RunScript("unregisterServiceWorker()", &script_result));
+ ASSERT_EQ("service worker unregistration status: true", script_result);
+ 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();
+ PushMessagingAppIdentifier app_identifier =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), origin,
+ 0LL /* service_worker_registration_id */);
+ EXPECT_TRUE(app_identifier.is_null());
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ ServiceWorkerDatabaseDeletionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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()->profile()), 0u);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ InvalidGetSubscriptionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ PushMessagingAppIdentifier app_identifier1 =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), 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.
+ PushMessagingAppIdentifier app_identifier2 =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), 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.
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ // 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.
+ PushMessagingAppIdentifier app_identifier3 =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), origin,
+ 0LL /* service_worker_registration_id */);
+ EXPECT_TRUE(app_identifier3.is_null());
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ GlobalResetPushPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - prompt", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ LocalResetPushPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, origin,
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_DEFAULT);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - prompt", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ DenyPushPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, origin,
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_BLOCK);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ GlobalResetNotificationsPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - prompt", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ LocalResetNotificationsPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, GURL(),
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_DEFAULT);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - prompt", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ DenyNotificationsPermissionUnsubscribes) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, GURL(),
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_BLOCK);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("false - not subscribed", script_result);
+
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.UnregistrationReason",
+ static_cast<int>(
+ blink::mojom::PushUnregistrationReason::PERMISSION_REVOKED),
+ 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingBrowserTest,
+ GrantAlreadyGrantedPermissionDoesNotUnsubscribe) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(1, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, GURL(),
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_ALLOW);
+
+ message_loop_runner->Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);
+}
+
+// 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner =
+ new content::MessageLoopRunner;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ base::BarrierClosure(2, message_loop_runner->QuitClosure()));
+
+ GURL origin = https_server()->GetURL("/").DeprecatedGetOriginAsURL();
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_ALLOW);
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(origin, GURL(),
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_DEFAULT);
+
+ message_loop_runner->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.
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ histogram_tester_.ExpectTotalCount("PushMessaging.UnregistrationReason", 0);
+}
+
+// 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()->profile()->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()->profile())
+ ->ClearSettingsForOneType(ContentSettingsType::NOTIFICATIONS);
+
+ run_loop.Run();
+
+ // |app_identifier| should no longer be stored in prefs.
+ PushMessagingAppIdentifier stored_app_identifier =
+ PushMessagingAppIdentifier::FindByAppId(GetBrowser()->profile(), 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;
+ ASSERT_TRUE(RunScript("GetP256dh()", &first_public_key));
+ EXPECT_GE(first_public_key.size(), 32u);
+
+ std::string script_result;
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ std::string token2;
+ ASSERT_NO_FATAL_FAILURE(
+ SubscribeSuccessfully(PushSubscriptionKeyFormat::kBinary, &token2));
+ EXPECT_NE(token1, token2);
+
+ std::string second_public_key;
+ ASSERT_TRUE(RunScript("GetP256dh()", &second_public_key));
+ 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_.SetUp(https_server());
+ PushMessagingBrowserTestBase::SetUpOnMainThread();
+ }
+ Browser* GetBrowser() const override { return incognito_browser_; }
+
+ content::WebContents* web_contents() {
+ return GetBrowser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ protected:
+ content::test::PrerenderTestHelper prerender_helper_;
+ raw_ptr<Browser> incognito_browser_ = nullptr;
+};
+
+// Regression test for https://crbug.com/476474
+IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest,
+ IncognitoGetSubscriptionDoesNotHang) {
+ ASSERT_TRUE(GetBrowser()->profile()->IsOffTheRecord());
+
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ // In Incognito mode the promise returned by getSubscription should not hang,
+ // it should just fulfill with null.
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ ASSERT_EQ("false - not subscribed", script_result);
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest, WarningToCorrectRFH) {
+ ASSERT_TRUE(GetBrowser()->profile()->IsOffTheRecord());
+
+ content::WebContentsConsoleObserver console_observer(web_contents());
+ console_observer.SetPattern(kIncognitoWarningPattern);
+
+ // Filter out the main frame host of the currently active page.
+ content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
+ console_observer.SetFilter(base::BindLambdaForTesting(
+ [&](const content::WebContentsConsoleObserver::Message& message) {
+ return message.source_frame == rfh;
+ }));
+
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_TRUE(RunScript("documentSubscribePush()", &script_result));
+ ASSERT_EQ("AbortError - Registration failed - permission denied",
+ script_result);
+
+ console_observer.Wait();
+ EXPECT_EQ(1u, console_observer.messages().size());
+}
+
+IN_PROC_BROWSER_TEST_F(PushMessagingIncognitoBrowserTest,
+ WarningToCorrectRFH_Prerender) {
+ ASSERT_TRUE(GetBrowser()->profile()->IsOffTheRecord());
+
+ const GURL url(https_server()->GetURL(GetTestURL()));
+
+ // Start a prerender with the push messaging test URL.
+ int host_id = prerender_helper_.AddPrerender(url);
+ content::test::PrerenderHostObserver prerender_observer(*web_contents(),
+ host_id);
+ ASSERT_NE(prerender_helper_.GetHostForUrl(url),
+ content::RenderFrameHost::kNoFrameTreeNodeId);
+
+ 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;
+ }));
+
+ std::string script_result;
+
+ ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+ prerender_rfh, "registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ // 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;
+ prerender_helper_.NavigatePrimaryPage(url);
+ // Make sure that the prerender was activated.
+ ASSERT_TRUE(prerender_observer.was_activated());
+ do {
+ ASSERT_TRUE(message_queue.WaitForMessage(&script_result));
+ } while (script_result !=
+ "\"AbortError - Registration failed - permission denied\"");
+
+ 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) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // 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.
+ ASSERT_TRUE(
+ RunScript("documentSubscribePushWithNumericKey()", &script_result));
+ EXPECT_EQ(
+ "AbortError - Registration failed - GCM Sender IDs are no longer "
+ "supported, please upgrade to VAPID authentication instead",
+ script_result);
+}
+
+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().ToJsTimeIgnoringNull() < output;
+}
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
+ SubscribeGetSubscriptionWithExpirationTime) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // Subscribe with expiration time enabled, should get a subscription with
+ // expiration time in the future back
+ std::string subscription_expiration_time;
+ ASSERT_TRUE(RunScript("documentSubscribePushGetExpirationTime()",
+ &subscription_expiration_time));
+ EXPECT_TRUE(IsExpirationTimeValid(subscription_expiration_time));
+
+ std::string get_subscription_expiration_time;
+ // Get subscription should also yield a subscription with expiration time
+ ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()",
+ &get_subscription_expiration_time));
+ 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) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ // Get subscription should also yield a subscription with expiration time
+ ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()", &script_result));
+ EXPECT_TRUE(IsExpirationTimeValid(script_result));
+}
+
+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) {
+ std::string script_result;
+
+ ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+ ASSERT_EQ("ok - service worker registered", script_result);
+
+ ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+ LoadTestPage(); // Reload to become controlled.
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ // When |features::kPushSubscriptionWithExpirationTime| is disabled,
+ // expiration time should be null
+ ASSERT_TRUE(
+ RunScript("documentSubscribePushGetExpirationTime()", &script_result));
+ EXPECT_EQ("null", script_result);
+}
+
+class PushSubscriptionChangeEventTest : public PushMessagingBrowserTestBase {
+ public:
+ PushSubscriptionChangeEventTest() {
+ scoped_feature_list_.InitWithFeatures(
+ {features::kPushSubscriptionChangeEvent,
+ features::kPushSubscriptionWithExpirationTime},
+ {});
+ }
+
+ ~PushSubscriptionChangeEventTest() override = default;
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
+ PushSubscriptionChangeEventSuccess) {
+ std::string script_result;
+
+ // Create the |old_subscription| by subscribing and unsubscribing again
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+
+ blink::mojom::PushSubscriptionPtr old_subscription =
+ GetSubscriptionForAppIdentifier(app_identifier);
+
+ ASSERT_TRUE(RunScript("unsubscribePush()", &script_result));
+ EXPECT_EQ("unsubscribe result: true", script_result);
+
+ // There should be no subscription since we unsubscribed
+ EXPECT_EQ(PushMessagingAppIdentifier::GetCount(GetBrowser()->profile()), 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_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ base::RunLoop run_loop;
+ push_service()->FirePushSubscriptionChange(
+ app_identifier, run_loop.QuitClosure(), std::move(new_subscription),
+ std::move(old_subscription));
+ run_loop.Run();
+
+ // Compare old subscription
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ(old_endpoint.spec(), script_result);
+ // Compare new subscription
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ(new_endpoint.spec(), script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.PushSubscriptionChangeStatus",
+ blink::mojom::PushEventStatus::SUCCESS, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest,
+ FiredAfterPermissionRevoked) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - granted", script_result);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ PushMessagingAppIdentifier app_identifier =
+ GetAppIdentifierForServiceWorkerRegistration(0LL);
+ auto old_subscription = GetSubscriptionForAppIdentifier(app_identifier);
+
+ base::RunLoop run_loop;
+ push_service()->SetContentSettingChangedCallbackForTesting(
+ run_loop.QuitClosure());
+ HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile())
+ ->SetContentSettingDefaultScope(app_identifier.origin(), GURL(),
+ ContentSettingsType::NOTIFICATIONS,
+ CONTENT_SETTING_BLOCK);
+ run_loop.Run();
+
+ ASSERT_TRUE(RunScript("pushManagerPermissionState()", &script_result));
+ EXPECT_EQ("permission status - denied", script_result);
+
+ // Check if the pushsubscriptionchangeevent arrived in the document and
+ // whether the |old_subscription| has the expected endpoint and
+ // |new_subscription| is null
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ(old_subscription->endpoint.spec(), script_result);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_EQ("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.PushSubscriptionChangeStatus",
+ blink::mojom::PushEventStatus::SUCCESS, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionChangeEventTest, OnInvalidation) {
+ std::string script_result;
+
+ ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+ ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+ EXPECT_EQ("true - subscribed", script_result);
+
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("false - is not controlled", script_result);
+ LoadTestPage(); // Reload to become controlled.
+ ASSERT_TRUE(RunScript("isControlled()", &script_result));
+ ASSERT_EQ("true - is controlled", script_result);
+
+ PushMessagingAppIdentifier 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
+ PushMessagingAppIdentifier deleted_identifier =
+ PushMessagingAppIdentifier::FindByAppId(GetBrowser()->profile(),
+ app_identifier.app_id());
+ EXPECT_TRUE(deleted_identifier.is_null());
+
+ // New subscription with a different app id should exist
+ PushMessagingAppIdentifier new_identifier =
+ PushMessagingAppIdentifier::FindByServiceWorker(
+ GetBrowser()->profile(), 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
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_NE("null", script_result);
+ ASSERT_TRUE(RunScript("resultQueue.pop()", &script_result));
+ EXPECT_NE("null", script_result);
+
+ // Check that we record this case in UMA.
+ histogram_tester_.ExpectUniqueSample(
+ "PushMessaging.PushSubscriptionChangeStatus",
+ blink::mojom::PushEventStatus::SUCCESS, 1);
+}