1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_SERVICE_IMPL_H_
#define CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_SERVICE_IMPL_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <queue>
#include <utility>
#include <vector>
#include "base/callback_list.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/permissions/permission_revocation_request.h"
#include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
#include "chrome/browser/push_messaging/push_messaging_refresher.h"
#include "chrome/common/buildflags.h"
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/gcm_driver/common/gcm_message.h"
#include "components/gcm_driver/crypto/gcm_encryption_provider.h"
#include "components/gcm_driver/gcm_app_handler.h"
#include "components/gcm_driver/gcm_client.h"
#include "components/gcm_driver/instance_id/instance_id.h"
#include "components/keyed_service/core/keyed_service.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/push_messaging_service.h"
#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-forward.h"
class GURL;
class PrefRegistrySimple;
class Profile;
class PushMessagingAppIdentifier;
class PushMessagingServiceTest;
class FCMRevocationTest;
class ScopedKeepAlive;
class ScopedProfileKeepAlive;
#if BUILDFLAG(IS_ANDROID)
class PrefService;
#endif
namespace blink {
namespace mojom {
enum class PushEventStatus;
enum class PushRegistrationStatus;
} // namespace mojom
} // namespace blink
namespace content {
class DevToolsBackgroundServicesContext;
} // namespace content
namespace gcm {
class GCMDriver;
} // namespace gcm
namespace instance_id {
class InstanceIDDriver;
} // namespace instance_id
namespace {
struct PendingMessage {
PendingMessage(std::string app_id, gcm::IncomingMessage message);
PendingMessage(const PendingMessage& other);
PendingMessage(PendingMessage&& other);
~PendingMessage();
PendingMessage& operator=(PendingMessage&& other);
std::string app_id;
gcm::IncomingMessage message;
base::Time received_time;
};
} // namespace
class PushMessagingServiceImpl : public content::PushMessagingService,
public gcm::GCMAppHandler,
public content_settings::Observer,
public KeyedService,
public PushMessagingRefresher::Observer {
public:
// If any Service Workers are using push, starts GCM and adds an app handler.
static void InitializeForProfile(Profile* profile);
explicit PushMessagingServiceImpl(Profile* profile);
PushMessagingServiceImpl(const PushMessagingServiceImpl&) = delete;
PushMessagingServiceImpl& operator=(const PushMessagingServiceImpl&) = delete;
~PushMessagingServiceImpl() override;
// Check and remove subscriptions that are expired when |this| is initialized
void RemoveExpiredSubscriptions();
// Gets the permission status for the given |origin|.
blink::mojom::PermissionStatus GetPermissionStatus(const GURL& origin,
bool user_visible);
#if BUILDFLAG(IS_ANDROID)
// Registers Local State prefs used by this class.
static void RegisterPrefs(PrefRegistrySimple* registry);
static void RevokePermissionIfPossible(GURL origin,
bool app_level_notifications_enabled,
PrefService* prefs,
Profile* profile);
#endif
// gcm::GCMAppHandler implementation.
void ShutdownHandler() override;
void OnStoreReset() override;
void OnMessage(const std::string& app_id,
const gcm::IncomingMessage& message) override;
void OnMessagesDeleted(const std::string& app_id) override;
void OnSendError(
const std::string& app_id,
const gcm::GCMClient::SendErrorDetails& send_error_details) override;
void OnSendAcknowledged(const std::string& app_id,
const std::string& message_id) override;
void OnMessageDecryptionFailed(const std::string& app_id,
const std::string& message_id,
const std::string& error_message) override;
bool CanHandle(const std::string& app_id) const override;
// content::PushMessagingService implementation:
void SubscribeFromDocument(const GURL& requesting_origin,
int64_t service_worker_registration_id,
int render_process_id,
int render_frame_id,
blink::mojom::PushSubscriptionOptionsPtr options,
bool user_gesture,
RegisterCallback callback) override;
void SubscribeFromWorker(const GURL& requesting_origin,
int64_t service_worker_registration_id,
int render_process_id,
blink::mojom::PushSubscriptionOptionsPtr options,
RegisterCallback callback) override;
void GetSubscriptionInfo(const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const std::string& subscription_id,
SubscriptionInfoCallback callback) override;
void Unsubscribe(blink::mojom::PushUnregistrationReason reason,
const GURL& requesting_origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
UnregisterCallback) override;
bool SupportNonVisibleMessages() override;
void DidDeleteServiceWorkerRegistration(
const GURL& origin,
int64_t service_worker_registration_id) override;
void DidDeleteServiceWorkerDatabase() override;
// content_settings::Observer implementation.
void OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) override;
// Fires the `pushsubscriptionchange` event to the service worker with
// `service_worker_registration_id` and `origin`. The two subscriptions
// `old_subscription` and `new_subscription` can be null.
void FirePushSubscriptionChange(
const GURL& origin,
int64_t service_worker_registration_id,
base::OnceClosure completed_closure,
blink::mojom::PushSubscriptionPtr new_subscription,
blink::mojom::PushSubscriptionPtr old_subscription);
// Fires the `pushsubscriptionchange` event to the associated service worker
// of |app_identifier|, which is the app identifier for |old_subscription|
// whereas |new_subscription| can be either null e.g. when a subscription is
// lost due to permission changes or a new subscription when it was refreshed.
void FirePushSubscriptionChangeForAppIdentifier(
const PushMessagingAppIdentifier& app_identifier,
base::OnceClosure completed_closure,
blink::mojom::PushSubscriptionPtr new_subscription,
blink::mojom::PushSubscriptionPtr old_subscription);
// KeyedService implementation.
void Shutdown() override;
// WARNING: Only call this function if features::kPushSubscriptionChangeEvent
// is enabled, will be later used by the Push Service to trigger subscription
// refreshes
void OnSubscriptionInvalidation(const std::string& app_id);
// PushMessagingRefresher::Observer implementation
// Initiate unsubscribe task when old subscription becomes invalid
void OnOldSubscriptionExpired(const std::string& app_id,
const std::string& sender_id) override;
void OnRefreshFinished(
const PushMessagingAppIdentifier& app_identifier) override;
// Sets a callback that can be used to listen for service worker
// subscription events.
void SetSubscribeFromWorkerCallback(
base::RepeatingCallback<void(/*registration id=*/int64_t)> callback);
void SetMessageCallbackForTesting(const base::RepeatingClosure& callback);
void SetUnsubscribeCallbackForTesting(base::OnceClosure callback);
void SetInvalidationCallbackForTesting(base::OnceClosure callback);
void SetContentSettingChangedCallbackForTesting(
base::RepeatingClosure callback);
void SetServiceWorkerUnregisteredCallbackForTesting(
base::RepeatingClosure callback);
void SetServiceWorkerDatabaseWipedCallbackForTesting(
base::RepeatingClosure callback);
void SetRemoveExpiredSubscriptionsCallbackForTesting(
base::OnceClosure closure);
private:
friend class PushMessagingBrowserTestBase;
friend class PushMessagingServiceTest;
friend class FCMRevocationTest;
FRIEND_TEST_ALL_PREFIXES(PushMessagingBrowserTest, PushEventOnShutdown);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, NormalizeSenderInfo);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, PayloadEncryptionTest);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest,
TestMultipleIncomingPushMessages);
#if BUILDFLAG(IS_ANDROID)
FRIEND_TEST_ALL_PREFIXES(FCMRevocationTest,
TestPermissionRevocationClearPreferences);
FRIEND_TEST_ALL_PREFIXES(FCMRevocationTest,
TestPermissionRevocationNoPermissionFirstMessage);
FRIEND_TEST_ALL_PREFIXES(FCMRevocationTest,
TestPermissionRevocationGracePeriodIsOver);
#endif
// A subscription is pending until it has succeeded or failed.
void IncreasePushSubscriptionCount(int add, bool is_pending);
void DecreasePushSubscriptionCount(int subtract, bool was_pending);
// OnMessage methods ---------------------------------------------------------
void DeliverMessageCallback(const std::string& app_id,
const GURL& requesting_origin,
int64_t service_worker_registration_id,
const gcm::IncomingMessage& message,
bool did_enqueue_message,
blink::mojom::PushEventStatus status);
void DidHandleEnqueuedMessage(
const GURL& origin,
int64_t service_worker_registration_id,
base::OnceCallback<void(bool)> message_handled_callback,
bool did_show_generic_notification);
void DidHandleMessage(const std::string& app_id,
const std::string& push_message_id,
bool did_show_generic_notification);
void OnCheckedOrigin(PendingMessage message,
PermissionRevocationRequest::Outcome outcome);
void DeliverNextQueuedMessageForServiceWorkerRegistration(
const GURL& origin,
int64_t service_worker_registration_id);
void CheckOriginAndDispatchNextMessage();
// Subscribe methods ---------------------------------------------------------
void DoSubscribe(PushMessagingAppIdentifier app_identifier,
blink::mojom::PushSubscriptionOptionsPtr options,
RegisterCallback callback,
int render_process_id,
int render_frame_id,
blink::mojom::PermissionStatus permission_status);
void SubscribeEnd(RegisterCallback callback,
const std::string& subscription_id,
const GURL& endpoint,
const std::optional<base::Time>& expiration_time,
const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth,
blink::mojom::PushRegistrationStatus status);
void SubscribeEndWithError(RegisterCallback callback,
blink::mojom::PushRegistrationStatus status);
void DidSubscribe(const PushMessagingAppIdentifier& app_identifier,
const std::string& sender_id,
RegisterCallback callback,
const std::string& subscription_id,
instance_id::InstanceID::Result result);
void DidSubscribeWithEncryptionInfo(
const PushMessagingAppIdentifier& app_identifier,
RegisterCallback callback,
const std::string& subscription_id,
const GURL& endpoint,
std::string p256dh,
std::string auth_secret);
// GetSubscriptionInfo methods -----------------------------------------------
void DidValidateSubscription(const std::string& app_id,
const std::string& sender_id,
const GURL& endpoint,
const std::optional<base::Time>& expiration_time,
SubscriptionInfoCallback callback,
bool is_valid);
void DidGetEncryptionInfo(const GURL& endpoint,
const std::optional<base::Time>& expiration_time,
SubscriptionInfoCallback callback,
std::string p256dh,
std::string auth_secret) const;
// Unsubscribe methods -------------------------------------------------------
// |origin|, |service_worker_registration_id| and |app_id| should be provided
// whenever they can be obtained. It's valid for |origin| to be empty and
// |service_worker_registration_id| to be kInvalidServiceWorkerRegistrationId,
// or for app_id to be empty, but not both at once.
void UnsubscribeInternal(blink::mojom::PushUnregistrationReason reason,
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& app_id,
const std::string& sender_id,
UnregisterCallback callback);
void DidClearPushSubscriptionId(blink::mojom::PushUnregistrationReason reason,
const std::string& app_id,
const std::string& sender_id,
UnregisterCallback callback);
void DidUnregister(bool was_subscribed, gcm::GCMClient::Result result);
void DidDeleteID(const std::string& app_id,
bool was_subscribed,
instance_id::InstanceID::Result result);
void DidUnsubscribe(const std::string& app_id_when_instance_id,
bool was_subscribed);
// OnContentSettingChanged methods -------------------------------------------
void GetPushSubscriptionFromAppIdentifier(
const PushMessagingAppIdentifier& app_identifier,
base::OnceCallback<void(blink::mojom::PushSubscriptionPtr)> callback);
void DidGetSWData(
const PushMessagingAppIdentifier& app_identifier,
base::OnceCallback<void(blink::mojom::PushSubscriptionPtr)> callback,
const std::string& sender_id,
const std::string& subscription_id);
void GetPushSubscriptionFromAppIdentifierEnd(
base::OnceCallback<void(blink::mojom::PushSubscriptionPtr)> callback,
const std::string& sender_id,
bool is_valid,
const GURL& endpoint,
const std::optional<base::Time>& expiration_time,
const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth);
// OnSubscriptionInvalidation methods-----------------------------------------
void GetOldSubscription(PushMessagingAppIdentifier old_app_identifier,
const std::string& sender_id);
// After gathering all relavent information to start the refresh,
// generate a new app id and initiate refresh
void StartRefresh(PushMessagingAppIdentifier old_app_identifier,
const std::string& sender_id,
blink::mojom::PushSubscriptionPtr old_subscription);
// Makes a new susbcription and replaces the old subscription by new
// subscription in preferences and service worker database
void UpdateSubscription(PushMessagingAppIdentifier app_identifier,
blink::mojom::PushSubscriptionOptionsPtr options,
RegisterCallback callback);
// After the subscription is updated, fire a `pushsubscriptionchange` event
// and notify the |refresher_|
void DidUpdateSubscription(const std::string& new_app_id,
const std::string& old_app_id,
blink::mojom::PushSubscriptionPtr old_subscription,
const std::string& sender_id,
const std::string& registration_id,
const GURL& endpoint,
const std::optional<base::Time>& expiration_time,
const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth,
blink::mojom::PushRegistrationStatus status);
// Helper methods ------------------------------------------------------------
// The subscription given in |identifier| will be unsubscribed (and a
// `pushsubscriptionchange` event fires if
// features::kPushSubscriptionChangeEvent is enabled)
void UnexpectedChange(PushMessagingAppIdentifier identifier,
blink::mojom::PushUnregistrationReason reason,
base::OnceClosure completed_closure);
void UnexpectedUnsubscribe(const PushMessagingAppIdentifier& app_identifier,
blink::mojom::PushUnregistrationReason reason,
UnregisterCallback unregister_callback);
void DidGetSenderIdUnexpectedUnsubscribe(
const PushMessagingAppIdentifier& app_identifier,
blink::mojom::PushUnregistrationReason reason,
UnregisterCallback callback,
const std::string& sender_id);
// Checks if a given origin is allowed to use Push.
//
// `user_visible` is the userVisibleOnly value provided to the push
// registration.
//
// For most origins this checks if the origin has the notifications
// permission. An exception is for extensions that use service workers where
// `user_visible` is false.
bool IsPermissionSet(const GURL& origin, bool user_visible);
// Wrapper around {GCMDriver, InstanceID}::GetEncryptionInfo.
void GetEncryptionInfoForAppId(
const std::string& app_id,
const std::string& sender_id,
gcm::GCMEncryptionProvider::EncryptionInfoCallback callback);
gcm::GCMDriver* GetGCMDriver() const;
instance_id::InstanceIDDriver* GetInstanceIDDriver() const;
content::DevToolsBackgroundServicesContext* GetDevToolsContext(
const GURL& origin) const;
// Testing methods -----------------------------------------------------------
using PushEventCallback =
base::OnceCallback<void(blink::mojom::PushEventStatus)>;
using MessageDispatchedCallback =
base::RepeatingCallback<void(const std::string& app_id,
const GURL& origin,
int64_t service_worker_registration_id,
std::optional<std::string> payload,
PushEventCallback callback)>;
// Callback to be invoked when a message has been dispatched. Enables tests to
// observe message delivery instead of delivering it to the Service Worker.
void SetMessageDispatchedCallbackForTesting(
const MessageDispatchedCallback& callback) {
message_dispatched_callback_for_testing_ = callback;
}
void OnAppTerminating();
raw_ptr<Profile> profile_;
std::unique_ptr<PermissionRevocationRequest> origin_revocation_request_;
std::queue<PendingMessage> messages_pending_permission_check_;
// {Origin, ServiceWokerRegistratonId} key for message delivery queue. This
// ensures that we only deliver one message at a time per ServiceWorker.
using MessageDeliveryQueueKey = std::pair<GURL, int64_t>;
// Queue of pending messages per ServiceWorkerRegstration to be delivered one
// at a time. This allows us to enforce visibility requirements.
base::flat_map<MessageDeliveryQueueKey, std::queue<PendingMessage>>
message_delivery_queue_;
int push_subscription_count_;
int pending_push_subscription_count_;
base::RepeatingClosure message_callback_for_testing_;
base::OnceClosure unsubscribe_callback_for_testing_;
base::RepeatingClosure content_setting_changed_callback_for_testing_;
base::RepeatingClosure service_worker_unregistered_callback_for_testing_;
base::RepeatingClosure service_worker_database_wiped_callback_for_testing_;
base::OnceClosure remove_expired_subscriptions_callback_for_testing_;
base::OnceClosure invalidation_callback_for_testing_;
PushMessagingNotificationManager notification_manager_;
PushMessagingRefresher refresher_;
base::ScopedObservation<PushMessagingRefresher,
PushMessagingRefresher::Observer>
refresh_observation_{this};
MessageDispatchedCallback message_dispatched_callback_for_testing_;
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
// KeepAlive registered while we have in-flight push messages, to make sure
// we can finish processing them without being interrupted by BrowserProcess
// teardown.
std::unique_ptr<ScopedKeepAlive> in_flight_keep_alive_;
// Same as ScopedKeepAlive, but prevents |profile_| from getting deleted.
std::unique_ptr<ScopedProfileKeepAlive> in_flight_profile_keep_alive_;
#endif
base::CallbackListSubscription on_app_terminating_subscription_;
// Callback used to be alerted of a new service worker subscription.
std::optional<base::RepeatingCallback<void(int64_t)>>
subscribe_from_worker_callback_;
// True when shutdown has started. Do not allow processing of incoming
// messages when this is true.
bool shutdown_started_ = false;
int render_process_id_ = content::ChildProcessHost::kInvalidUniqueID;
// Tracks those that are attempting to bypass the user visible
// requirement on push notifications. E.g. they set userVisibleOnly to false
// on push registration.
std::set<GURL> origins_requesting_user_visible_requirement_bypass;
base::WeakPtrFactory<PushMessagingServiceImpl> weak_factory_{this};
};
#endif // CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_SERVICE_IMPL_H_
|