Index: chrome/browser/push_messaging/push_messaging_service_impl.cc |
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc |
index 1cc200aaa054c6c024e07709450e1b4831dab4c3..fbe657c04e8b434d1f0b076f65d9b8e5d6d5cd69 100644 |
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc |
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc |
@@ -83,6 +83,11 @@ void RecordDeliveryStatus(content::PushDeliveryStatus status) { |
content::PUSH_DELIVERY_STATUS_LAST + 1); |
} |
+void RecordUnsubscribeReason(content::PushUnregistrationReason reason) { |
+ UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationReason", reason, |
+ content::PUSH_UNREGISTRATION_REASON_LAST + 1); |
+} |
+ |
blink::WebPushPermissionStatus ToPushPermission( |
blink::mojom::PermissionStatus permission_status) { |
switch (permission_status) { |
@@ -98,8 +103,49 @@ blink::WebPushPermissionStatus ToPushPermission( |
} |
} |
+content::PushUnregistrationStatus ToUnregisterStatus( |
+ InstanceID::Result result) { |
+ switch (result) { |
+ case InstanceID::SUCCESS: |
+ return content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED; |
+ case InstanceID::INVALID_PARAMETER: |
+ case InstanceID::DISABLED: |
+ case InstanceID::SERVER_ERROR: |
+ case InstanceID::UNKNOWN_ERROR: |
+ return content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR; |
+ case InstanceID::ASYNC_OPERATION_PENDING: |
+ case InstanceID::NETWORK_ERROR: |
+ return content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR; |
+ } |
Peter Beverloo
2016/09/30 14:26:06
Most compilers will want you to return a default v
johnme
2016/09/30 17:02:09
No, default is evil! It prevents compilers from wa
|
+} |
+ |
+content::PushUnregistrationStatus ToUnregisterStatus( |
+ gcm::GCMClient::Result gcm_result) { |
+ switch (gcm_result) { |
+ case gcm::GCMClient::SUCCESS: |
+ return content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED; |
+ case gcm::GCMClient::INVALID_PARAMETER: |
+ case gcm::GCMClient::GCM_DISABLED: |
+ case gcm::GCMClient::SERVER_ERROR: |
+ case gcm::GCMClient::UNKNOWN_ERROR: |
+ return content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR; |
+ case gcm::GCMClient::ASYNC_OPERATION_PENDING: |
+ case gcm::GCMClient::NETWORK_ERROR: |
+ case gcm::GCMClient::TTL_EXCEEDED: |
+ return content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR; |
+ } |
+} |
+ |
+void GCMCallbackToUnregisterCallback( |
+ const content::PushMessagingService::UnregisterCallback& callback, |
+ gcm::GCMClient::Result result) { |
+ DCHECK(!callback.is_null()); |
+ callback.Run(ToUnregisterStatus(result)); |
+} |
+ |
void UnregisterCallbackToClosure(const base::Closure& closure, |
content::PushUnregistrationStatus status) { |
+ DCHECK(!closure.is_null()); |
closure.Run(); |
} |
@@ -236,8 +282,9 @@ void PushMessagingServiceImpl::OnMessage(const std::string& app_id, |
PushMessagingAppIdentifier::FindByAppId(profile_, app_id); |
// Drop message and unregister if app_id was unknown (maybe recently deleted). |
if (app_identifier.is_null()) { |
- DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message, |
- message_handled_closure, |
+ DeliverMessageCallback(app_id, GURL::EmptyGURL(), |
+ -1 /* kInvalidServiceWorkerRegistrationId */, |
+ message, message_handled_closure, |
content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID); |
return; |
} |
@@ -287,6 +334,8 @@ void PushMessagingServiceImpl::DeliverMessageCallback( |
content::PushDeliveryStatus status) { |
DCHECK_GE(in_flight_message_deliveries_.count(app_id), 1u); |
+ RecordDeliveryStatus(status); |
+ |
base::Closure completion_closure = |
base::Bind(&PushMessagingServiceImpl::DidHandleMessage, |
weak_factory_.GetWeakPtr(), app_id, message_handled_closure); |
@@ -294,6 +343,13 @@ void PushMessagingServiceImpl::DeliverMessageCallback( |
// unless it is explicitly passed to another function. |
base::ScopedClosureRunner completion_closure_runner(completion_closure); |
+ // Dummy reason meaning this function should not automatically unsubscribe. |
+ const content::PushUnregistrationReason DO_NOT_UNSUBSCRIBE = |
+ content::PUSH_UNREGISTRATION_REASON_JAVASCRIPT_API; |
Peter Beverloo
2016/09/30 14:26:06
What about defining a PUSH_UNREGISTRATION_REASON_U
johnme
2016/09/30 17:02:10
Done.
|
+ |
+ // Whether and why automatic unsubscription is necessary. |
+ content::PushUnregistrationReason unsubscribe_reason = DO_NOT_UNSUBSCRIBE; |
+ |
// TODO(mvanouwerkerk): Show a warning in the developer console of the |
// Service Worker corresponding to app_id (and/or on an internals page). |
// See https://crbug.com/508516 for options. |
@@ -318,17 +374,35 @@ void PushMessagingServiceImpl::DeliverMessageCallback( |
#endif |
break; |
case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR: |
+ // Do nothing, and hope the error is transient. |
break; |
case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID: |
+ unsubscribe_reason = |
+ content::PUSH_UNREGISTRATION_REASON_DELIVERY_UNKNOWN_APP_ID; |
+ break; |
case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED: |
+ unsubscribe_reason = |
+ content::PUSH_UNREGISTRATION_REASON_DELIVERY_PERMISSION_DENIED; |
+ break; |
case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER: |
- Unsubscribe(app_id, message.sender_id, |
- base::Bind(&UnregisterCallbackToClosure, |
- completion_closure_runner.Release())); |
+ unsubscribe_reason = |
+ content::PUSH_UNREGISTRATION_REASON_DELIVERY_NO_SERVICE_WORKER; |
break; |
} |
- RecordDeliveryStatus(status); |
+ if (unsubscribe_reason != DO_NOT_UNSUBSCRIBE) { |
+ PushMessagingAppIdentifier app_identifier = |
+ PushMessagingAppIdentifier::FindByAppId(profile_, app_id); |
+ UnsubscribeInternal( |
+ unsubscribe_reason, |
+ app_identifier.is_null() ? GURL::EmptyGURL() : app_identifier.origin(), |
+ app_identifier.is_null() |
+ ? -1 /* kInvalidServiceWorkerRegistrationId */ |
+ : app_identifier.service_worker_registration_id(), |
+ app_id, message.sender_id, |
+ base::Bind(&UnregisterCallbackToClosure, |
+ completion_closure_runner.Release())); |
+ } |
} |
void PushMessagingServiceImpl::DidHandleMessage( |
@@ -614,19 +688,54 @@ void PushMessagingServiceImpl::Unsubscribe( |
PushMessagingAppIdentifier app_identifier = |
PushMessagingAppIdentifier::FindByServiceWorker( |
profile_, requesting_origin, service_worker_registration_id); |
- if (app_identifier.is_null()) { |
- callback.Run( |
- content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED); |
+ |
+ UnsubscribeInternal( |
+ content::PUSH_UNREGISTRATION_REASON_JAVASCRIPT_API, requesting_origin, |
+ service_worker_registration_id, |
+ app_identifier.is_null() ? std::string() : app_identifier.app_id(), |
+ sender_id, callback); |
+} |
+ |
+void PushMessagingServiceImpl::UnsubscribeInternal( |
+ content::PushUnregistrationReason reason, |
+ const GURL& origin, |
+ int64_t service_worker_registration_id, |
+ const std::string& app_id, |
+ const std::string& sender_id, |
+ const content::PushMessagingService::UnregisterCallback& callback) { |
+ DCHECK(!app_id.empty() || (!origin.is_empty() && |
+ service_worker_registration_id != |
+ -1 /* kInvalidServiceWorkerRegistrationId */)) |
+ << "Need an app_id and/or origin+service_worker_registration_id"; |
+ |
+ RecordUnsubscribeReason(reason); |
+ |
+ if (origin.is_empty() || |
+ service_worker_registration_id == |
+ -1 /* kInvalidServiceWorkerRegistrationId */) { |
+ // Can't clear Service Worker database. |
+ DidClearPushSubscriptionId(reason, app_id, sender_id, callback); |
return; |
} |
- |
- Unsubscribe(app_identifier.app_id(), sender_id, callback); |
+ ClearPushSubscriptionId( |
+ profile_, origin, service_worker_registration_id, |
+ base::Bind(&PushMessagingServiceImpl::DidClearPushSubscriptionId, |
+ weak_factory_.GetWeakPtr(), reason, app_id, sender_id, |
+ callback)); |
} |
-void PushMessagingServiceImpl::Unsubscribe( |
+void PushMessagingServiceImpl::DidClearPushSubscriptionId( |
+ content::PushUnregistrationReason reason, |
const std::string& app_id, |
const std::string& sender_id, |
const content::PushMessagingService::UnregisterCallback& callback) { |
Peter Beverloo
2016/09/30 14:26:06
Specific to UnregisterCallback, since this is the
Peter Beverloo
2016/09/30 14:26:06
We probably want to have a `using` statement in th
johnme
2016/09/30 17:02:10
I just removed all occurrences of (content::)?Push
johnme
2016/09/30 17:02:10
I just removed all occurrences of (content::)?Push
|
+ if (app_id.empty()) { |
+ // There's nothing more we can do. |
Peter Beverloo
2016/09/30 14:26:06
// We cannot unsubscribe with the GCM Driver witho
johnme
2016/09/30 17:02:10
Done (expanded to cover PushMessagingAppIdentifier
|
+ callback.Run( |
+ content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED); |
+ return; |
+ } |
+ |
// Delete the mapping for this app_id, to guarantee that no messages get |
// delivered in future (even if unregistration fails). |
// TODO(johnme): Instead of deleting these app ids, store them elsewhere, and |
@@ -641,10 +750,14 @@ void PushMessagingServiceImpl::Unsubscribe( |
GetInstanceIDDriver()->GetInstanceID(app_id)->DeleteID(base::Bind( |
&PushMessagingServiceImpl::DidDeleteID, weak_factory_.GetWeakPtr(), |
app_id, was_subscribed, callback)); |
+ |
} else { |
auto unregister_callback = |
- base::Bind(&PushMessagingServiceImpl::DidUnsubscribe, |
- weak_factory_.GetWeakPtr(), was_subscribed, callback); |
+ base::Bind(&GCMCallbackToUnregisterCallback, |
+ base::Bind(&PushMessagingServiceImpl::DidUnsubscribe, |
+ weak_factory_.GetWeakPtr(), |
+ std::string() /* app_id_if_was_instance_id */, |
+ was_subscribed, callback)); |
#if defined(OS_ANDROID) |
// On Android the backend is different, and requires the original sender_id. |
// UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one. |
@@ -665,78 +778,33 @@ void PushMessagingServiceImpl::DidDeleteID( |
bool was_subscribed, |
const content::PushMessagingService::UnregisterCallback& callback, |
InstanceID::Result result) { |
- // DidUnsubscribeInstanceID must be run asynchronously, since it calls |
+ // DidUnsubscribe must be run asynchronously when passing a non-empty |
+ // |app_id_if_was_instance_id|, since it calls |
// InstanceIDDriver::RemoveInstanceID which deletes the InstanceID itself. |
// Calling that immediately would cause a use-after-free in our caller. |
base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, base::Bind(&PushMessagingServiceImpl::DidUnsubscribeInstanceID, |
+ FROM_HERE, base::Bind(&PushMessagingServiceImpl::DidUnsubscribe, |
weak_factory_.GetWeakPtr(), app_id, was_subscribed, |
- callback, result)); |
-} |
- |
-void PushMessagingServiceImpl::DidUnsubscribeInstanceID( |
- const std::string& app_id, |
- bool was_subscribed, |
- const content::PushMessagingService::UnregisterCallback& callback, |
- InstanceID::Result result) { |
- GetInstanceIDDriver()->RemoveInstanceID(app_id); |
- |
- if (was_subscribed) |
- DecreasePushSubscriptionCount(1, false /* was_pending */); |
- |
- DCHECK(!callback.is_null()); |
- |
- if (!was_subscribed) { |
- callback.Run( |
- content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED); |
- return; |
- } |
- switch (result) { |
- case InstanceID::SUCCESS: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED); |
- break; |
- case InstanceID::INVALID_PARAMETER: |
- case InstanceID::DISABLED: |
- case InstanceID::SERVER_ERROR: |
- case InstanceID::UNKNOWN_ERROR: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR); |
- break; |
- case InstanceID::ASYNC_OPERATION_PENDING: |
- case InstanceID::NETWORK_ERROR: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR); |
- break; |
- } |
+ callback, ToUnregisterStatus(result))); |
} |
void PushMessagingServiceImpl::DidUnsubscribe( |
+ const std::string& app_id_if_was_instance_id, |
bool was_subscribed, |
const content::PushMessagingService::UnregisterCallback& callback, |
- gcm::GCMClient::Result gcm_result) { |
- if (was_subscribed) |
- DecreasePushSubscriptionCount(1, false /* was_pending */); |
+ content::PushUnregistrationStatus status) { |
+ if (!app_id_if_was_instance_id.empty()) |
+ GetInstanceIDDriver()->RemoveInstanceID(app_id_if_was_instance_id); |
DCHECK(!callback.is_null()); |
- if (!was_subscribed) { |
+ if (was_subscribed) { |
+ DecreasePushSubscriptionCount(1, false /* was_pending */); |
+ callback.Run(status); |
+ } else { |
+ // Override status since there was no record of an existing subscription. |
callback.Run( |
content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED); |
- return; |
- } |
- switch (gcm_result) { |
- case gcm::GCMClient::SUCCESS: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED); |
- break; |
- case gcm::GCMClient::INVALID_PARAMETER: |
- case gcm::GCMClient::GCM_DISABLED: |
- case gcm::GCMClient::SERVER_ERROR: |
- case gcm::GCMClient::UNKNOWN_ERROR: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR); |
- break; |
- case gcm::GCMClient::ASYNC_OPERATION_PENDING: |
- case gcm::GCMClient::NETWORK_ERROR: |
- case gcm::GCMClient::TTL_EXCEEDED: |
- callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR); |
- break; |
} |
} |
@@ -787,38 +855,36 @@ void PushMessagingServiceImpl::OnContentSettingChanged( |
app_identifier.service_worker_registration_id(), |
base::Bind( |
&PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked, |
- weak_factory_.GetWeakPtr(), app_identifier, barrier_closure)); |
+ weak_factory_.GetWeakPtr(), app_identifier, |
+ base::Bind(&UnregisterCallbackToClosure, barrier_closure))); |
} else { |
- UnsubscribeBecausePermissionRevoked( |
- app_identifier, barrier_closure, "" /* sender_id */, |
- true /* success */, true /* not_found */); |
+ UnsubscribeInternal( |
+ content::PUSH_UNREGISTRATION_REASON_PERMISSION_REVOKED, |
+ app_identifier.origin(), |
+ app_identifier.service_worker_registration_id(), |
+ app_identifier.app_id(), std::string() /* sender_id */, |
+ base::Bind(&UnregisterCallbackToClosure, barrier_closure)); |
} |
} |
} |
void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked( |
const PushMessagingAppIdentifier& app_identifier, |
- const base::Closure& closure, |
+ const content::PushMessagingService::UnregisterCallback& callback, |
const std::string& sender_id, |
bool success, |
bool not_found) { |
- base::Closure barrier_closure = base::BarrierClosure(2, closure); |
- |
// Unsubscribe the PushMessagingAppIdentifier with the push service. |
// It's possible for GetSenderId to have failed and sender_id to be empty, if |
// cookies (and the SW database) for an origin got cleared before permissions |
// are cleared for the origin. In that case for legacy GCM registrations on |
// Android, Unsubscribe will just delete the app identifier to block future |
// messages. |
- // TODO(johnme): Auto-unregister before SW DB is cleared |
- // (https://crbug.com/402458). |
- Unsubscribe(app_identifier.app_id(), sender_id, |
- base::Bind(&UnregisterCallbackToClosure, barrier_closure)); |
- |
- // Clear the associated service worker push registration id. |
- ClearPushSubscriptionID(profile_, app_identifier.origin(), |
- app_identifier.service_worker_registration_id(), |
- barrier_closure); |
+ // TODO(johnme): Auto-unregister before SW DB is cleared (crbug.com/402458). |
+ UnsubscribeInternal(content::PUSH_UNREGISTRATION_REASON_PERMISSION_REVOKED, |
+ app_identifier.origin(), |
+ app_identifier.service_worker_registration_id(), |
+ app_identifier.app_id(), sender_id, callback); |
} |
void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting( |