blob: bbe679db01ef74779fea8f484241dd8979ea5f2c [file] [log] [blame]
// 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.
#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 <set>
#include <vector>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
#include "chrome/common/buildflags.h"
#include "components/content_settings/core/browser/content_settings_observer.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_messages.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/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/push_messaging_service.h"
class Profile;
class PushMessagingAppIdentifier;
class PushMessagingServiceTest;
class ScopedKeepAlive;
struct PushSubscriptionOptions;
namespace content {
namespace mojom {
enum class PushDeliveryStatus;
enum class PushRegistrationStatus;
} // namespace mojom
} // namespace content
namespace gcm {
class GCMDriver;
}
namespace instance_id {
class InstanceIDDriver;
}
class PushMessagingServiceImpl : public content::PushMessagingService,
public gcm::GCMAppHandler,
public content_settings::Observer,
public KeyedService,
public content::NotificationObserver {
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() override;
// Gets the permission status for the given |origin|.
blink::mojom::PermissionStatus GetPermissionStatus(const GURL& origin,
bool user_visible);
// 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;
bool CanHandle(const std::string& app_id) const override;
// content::PushMessagingService implementation:
GURL GetEndpoint(bool standard_protocol) const override;
void SubscribeFromDocument(const GURL& requesting_origin,
int64_t service_worker_registration_id,
int renderer_id,
int render_frame_id,
const content::PushSubscriptionOptions& options,
bool user_gesture,
const RegisterCallback& callback) override;
void SubscribeFromWorker(const GURL& requesting_origin,
int64_t service_worker_registration_id,
const content::PushSubscriptionOptions& options,
const RegisterCallback& callback) override;
void GetSubscriptionInfo(const GURL& origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const std::string& subscription_id,
const SubscriptionInfoCallback& callback) override;
void Unsubscribe(content::mojom::PushUnregistrationReason reason,
const GURL& requesting_origin,
int64_t service_worker_registration_id,
const std::string& sender_id,
const 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,
ContentSettingsType content_type,
const std::string& resource_identifier) override;
// KeyedService implementation.
void Shutdown() override;
// content::NotificationObserver:
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
void SetMessageCallbackForTesting(const base::Closure& callback);
void SetUnsubscribeCallbackForTesting(const base::Closure& callback);
void SetContentSettingChangedCallbackForTesting(
const base::Closure& callback);
void SetServiceWorkerUnregisteredCallbackForTesting(
const base::Closure& callback);
void SetServiceWorkerDatabaseWipedCallbackForTesting(
const base::Closure& callback);
private:
friend class PushMessagingBrowserTest;
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, NormalizeSenderInfo);
FRIEND_TEST_ALL_PREFIXES(PushMessagingServiceTest, PayloadEncryptionTest);
// 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,
const base::Closure& message_handled_closure,
content::mojom::PushDeliveryStatus status);
void DidHandleMessage(const std::string& app_id,
const base::Closure& completion_closure);
// Subscribe methods ---------------------------------------------------------
void DoSubscribe(const PushMessagingAppIdentifier& app_identifier,
const content::PushSubscriptionOptions& options,
const RegisterCallback& callback,
ContentSetting permission_status);
void SubscribeEnd(const RegisterCallback& callback,
const std::string& subscription_id,
const std::vector<uint8_t>& p256dh,
const std::vector<uint8_t>& auth,
content::mojom::PushRegistrationStatus status);
void SubscribeEndWithError(const RegisterCallback& callback,
content::mojom::PushRegistrationStatus status);
void DidSubscribe(const PushMessagingAppIdentifier& app_identifier,
const std::string& sender_id,
const RegisterCallback& callback,
const std::string& subscription_id,
instance_id::InstanceID::Result result);
void DidSubscribeWithEncryptionInfo(
const PushMessagingAppIdentifier& app_identifier,
const RegisterCallback& callback,
const std::string& subscription_id,
const std::string& p256dh,
const std::string& auth_secret);
// GetSubscriptionInfo methods -----------------------------------------------
void DidValidateSubscription(const std::string& app_id,
const std::string& sender_id,
const SubscriptionInfoCallback& callback,
bool is_valid);
void DidGetEncryptionInfo(const SubscriptionInfoCallback& callback,
const std::string& p256dh,
const 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(content::mojom::PushUnregistrationReason reason,
const GURL& origin,
int64_t service_worker_registration_id,
const std::string& app_id,
const std::string& sender_id,
const UnregisterCallback& callback);
void DidClearPushSubscriptionId(
content::mojom::PushUnregistrationReason reason,
const std::string& app_id,
const std::string& sender_id,
const 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 UnsubscribeBecausePermissionRevoked(
const PushMessagingAppIdentifier& app_identifier,
const UnregisterCallback& callback,
const std::string& sender_id,
bool success,
bool not_found);
// Helper methods ------------------------------------------------------------
// Normalizes the |sender_info|. In most cases the |sender_info| will be
// passed through to the GCM Driver as-is, but NIST P-256 application server
// keys have to be encoded using the URL-safe variant of the base64 encoding.
std::string NormalizeSenderInfo(const std::string& sender_info) const;
// Checks if a given origin is allowed to use Push.
bool IsPermissionSet(const GURL& origin);
// 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;
// Testing methods -----------------------------------------------------------
// Callback to be invoked when a message has been dispatched. Enables tests to
// observe message delivery before it's dispatched to the Service Worker.
using MessageDispatchedCallback =
base::Callback<void(const std::string& app_id,
const GURL& origin,
int64_t service_worker_registration_id,
base::Optional<std::string> payload)>;
void SetMessageDispatchedCallbackForTesting(
const MessageDispatchedCallback& callback) {
message_dispatched_callback_for_testing_ = callback;
}
Profile* profile_;
int push_subscription_count_;
int pending_push_subscription_count_;
base::Closure message_callback_for_testing_;
base::Closure unsubscribe_callback_for_testing_;
base::Closure content_setting_changed_callback_for_testing_;
base::Closure service_worker_unregistered_callback_for_testing_;
base::Closure service_worker_database_wiped_callback_for_testing_;
PushMessagingNotificationManager notification_manager_;
// A multiset containing one entry for each in-flight push message delivery,
// keyed by the receiver's app id.
std::multiset<std::string> in_flight_message_deliveries_;
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.
std::unique_ptr<ScopedKeepAlive> in_flight_keep_alive_;
#endif
content::NotificationRegistrar registrar_;
// True when shutdown has started. Do not allow processing of incoming
// messages when this is true.
bool shutdown_started_ = false;
base::WeakPtrFactory<PushMessagingServiceImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(PushMessagingServiceImpl);
};
#endif // CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_SERVICE_IMPL_H_