Chromium Code Reviews| Index: content/browser/push_messaging/push_messaging_message_filter.cc |
| diff --git a/content/browser/push_messaging/push_messaging_message_filter.cc b/content/browser/push_messaging/push_messaging_message_filter.cc |
| index 9820f4f151d95be74ca3f6e4ad5b6fc6a80158dc..da84a15df75ef5428f31f76f472390acd2ecb37a 100644 |
| --- a/content/browser/push_messaging/push_messaging_message_filter.cc |
| +++ b/content/browser/push_messaging/push_messaging_message_filter.cc |
| @@ -51,37 +51,30 @@ const char kIncognitoPushUnsupportedMessage[] = |
| "feature-detect this, since incognito mode needs to be undetectable by " |
| "websites."; |
| -// These UMA methods are only called from IO thread, but it would be acceptable |
| -// (even though slightly racy) to call them from UI thread as well, see |
| +// These UMA methods are called from the IO and/or UI threads. Racey but ok, see |
| // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/FNzZRJtN2aw/Aw0CWAXJJ1kJ |
| void RecordRegistrationStatus(PushRegistrationStatus status) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || |
| + BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| UMA_HISTOGRAM_ENUMERATION("PushMessaging.RegistrationStatus", status, |
| PUSH_REGISTRATION_STATUS_LAST + 1); |
| } |
| - |
| void RecordUnregistrationStatus(PushUnregistrationStatus status) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationStatus", status, |
| PUSH_UNREGISTRATION_STATUS_LAST + 1); |
| } |
| - |
| void RecordGetRegistrationStatus(PushGetRegistrationStatus status) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) || |
| + BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| UMA_HISTOGRAM_ENUMERATION("PushMessaging.GetRegistrationStatus", status, |
| PUSH_GETREGISTRATION_STATUS_LAST + 1); |
| } |
| -// Curries the |success| and |p256dh| parameters over to |callback| and |
| -// posts a task to invoke |callback| on the IO thread. |
| -void ForwardEncryptionInfoToIOThreadProxy( |
| - const PushMessagingService::EncryptionInfoCallback& callback, |
| - bool success, |
| - const std::vector<uint8_t>& p256dh, |
| - const std::vector<uint8_t>& auth) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| - base::Bind(callback, success, p256dh, auth)); |
| +void UnregisterCallbackToClosure(const base::Closure& closure, |
| + PushUnregistrationStatus status) { |
| + DCHECK(!closure.is_null()); |
| + closure.Run(); |
| } |
| // Returns whether |sender_info| contains a valid application server key, that |
| @@ -130,6 +123,14 @@ class PushMessagingMessageFilter::Core { |
| // Public Register methods on UI thread -------------------------------------- |
| + // Callback called on UI thread. |
| + void SubscribeDidGetInfoOnUI(const RegisterData& data, |
| + const std::string& push_subscription_id, |
| + const std::string& sender_id, |
| + bool is_valid, |
| + const std::vector<uint8_t>& p256dh, |
| + const std::vector<uint8_t>& auth); |
| + |
| // Called via PostTask from IO thread. |
| void RegisterOnUI(const RegisterData& data); |
| @@ -141,6 +142,24 @@ class PushMessagingMessageFilter::Core { |
| const GURL& requesting_origin, |
| const std::string& sender_id); |
| + // Public GetSubscription methods on UI thread ------------------------------- |
| + |
| + // Callback called on UI thread. |
| + void GetSubscriptionDidGetInfoOnUI(int request_id, |
| + const GURL& origin, |
| + int64_t service_worker_registration_id, |
| + const GURL& endpoint, |
| + const std::string& sender_info, |
| + bool is_valid, |
| + const std::vector<uint8_t>& p256dh, |
| + const std::vector<uint8_t>& auth); |
| + |
| + // Callback called on UI thread. |
| + void GetSubscriptionDidUnsubscribe( |
| + int request_id, |
| + PushGetRegistrationStatus get_status, |
| + PushUnregistrationStatus unsubscribe_status); |
| + |
| // Public GetPermission methods on UI thread --------------------------------- |
| // Called via PostTask from IO thread. |
| @@ -150,13 +169,13 @@ class PushMessagingMessageFilter::Core { |
| // Public helper methods on UI thread ---------------------------------------- |
| - // Called via PostTask from IO thread. The |io_thread_callback| callback |
| - // will be invoked on the IO thread. |
| - void GetEncryptionInfoOnUI( |
| + // Called via PostTask from IO thread. |callback| will be run on UI thread. |
| + void GetSubscriptionInfoOnUI( |
| const GURL& origin, |
| int64_t service_worker_registration_id, |
| const std::string& sender_id, |
| - const PushMessagingService::EncryptionInfoCallback& io_thread_callback); |
| + const std::string& push_subscription_id, |
| + const PushMessagingService::SubscriptionInfoCallback& callback); |
| // Called (directly) from both the UI and IO threads. |
| bool is_incognito() const { return is_incognito_; } |
| @@ -164,6 +183,9 @@ class PushMessagingMessageFilter::Core { |
| // Returns a push messaging service. May return null. |
| PushMessagingService* service(); |
| + // Returns a weak ptr. Must only be called from the outer class's constructor. |
| + base::WeakPtr<Core> GetWeakPtrFromIOParentConstructor(); |
| + |
| private: |
| friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; |
| friend class base::DeleteHelper<Core>; |
| @@ -239,6 +261,7 @@ PushMessagingMessageFilter::PushMessagingMessageFilter( |
| // constructor finishes. |
| ui_core_.reset( |
| new Core(weak_factory_io_to_io_.GetWeakPtr(), render_process_id)); |
| + ui_core_weak_ptr_ = ui_core_->GetWeakPtrFromIOParentConstructor(); |
| PushMessagingService* service = ui_core_->service(); |
| service_available_ = !!service; |
| @@ -316,7 +339,7 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration( |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (service_worker_status == SERVICE_WORKER_OK) { |
| DCHECK_EQ(2u, push_registration_id_and_sender_id.size()); |
| - const auto& push_registration_id = push_registration_id_and_sender_id[0]; |
| + const auto& push_subscription_id = push_registration_id_and_sender_id[0]; |
| const auto& stored_sender_id = push_registration_id_and_sender_id[1]; |
| std::string fixed_sender_id = |
| FixSenderInfo(data.options.sender_info, stored_sender_id); |
| @@ -328,15 +351,14 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration( |
| SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_SENDER_ID_MISMATCH); |
| return; |
| } |
| - auto callback = base::Bind( |
| - &PushMessagingMessageFilter::DidGetEncryptionKeys, |
| - weak_factory_io_to_io_.GetWeakPtr(), data, push_registration_id); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| - base::Bind(&Core::GetEncryptionInfoOnUI, |
| + base::Bind(&Core::GetSubscriptionInfoOnUI, |
| base::Unretained(ui_core_.get()), data.requesting_origin, |
| data.service_worker_registration_id, fixed_sender_id, |
| - callback)); |
| + push_subscription_id, |
| + base::Bind(&Core::SubscribeDidGetInfoOnUI, ui_core_weak_ptr_, |
| + data, push_subscription_id, fixed_sender_id))); |
| return; |
| } |
| // TODO(johnme): The spec allows the register algorithm to reject with an |
| @@ -358,21 +380,46 @@ void PushMessagingMessageFilter::DidCheckForExistingRegistration( |
| } |
| } |
| -void PushMessagingMessageFilter::DidGetEncryptionKeys( |
| +void PushMessagingMessageFilter::Core::SubscribeDidGetInfoOnUI( |
| const RegisterData& data, |
| - const std::string& push_registration_id, |
| - bool success, |
| + const std::string& push_subscription_id, |
| + const std::string& sender_id, |
| + bool is_valid, |
| const std::vector<uint8_t>& p256dh, |
| const std::vector<uint8_t>& auth) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - if (!success) { |
| - SendSubscriptionError( |
| - data, PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE); |
| - return; |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (is_valid) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PushMessagingMessageFilter::SendSubscriptionSuccess, |
| + io_parent_, data, |
| + PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE, |
| + push_subscription_id, p256dh, auth)); |
| + } else { |
| + // Uh-oh! Although there was a cached subscription in the Service Worker |
| + // database, it did not have matching counterparts in the |
| + // PushMessagingAppIdentifier map and/or GCM Store. Unsubscribe and |
| + // re-subscribe to fix this inconsistency. |
| + |
| + // Consider this subscription attempt to have failed. The re-subscribe will |
| + // be logged to UMA as a separate subscription attempt. |
| + RecordRegistrationStatus(PUSH_REGISTRATION_STATUS_STORAGE_CORRUPT); |
| + |
| + PushMessagingService* push_service = service(); |
| + DCHECK(push_service); // NOT_FOUND response can only come from service. |
|
Peter Beverloo
2017/03/20 23:50:13
Core::GetSubscriptionInfoOnUI specifically returns
johnme
2017/03/30 18:36:38
Hmm yeah. In practice it shouldn't be possible to
|
| + push_service->Unsubscribe( |
| + PUSH_UNREGISTRATION_REASON_SUBSCRIBE_STORAGE_CORRUPT, |
| + data.requesting_origin, data.service_worker_registration_id, sender_id, |
| + base::Bind(&UnregisterCallbackToClosure, |
| + base::Bind(IgnoreResult(&BrowserThread::PostTask), |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PushMessagingMessageFilter:: |
| + DidCheckForExistingRegistration, |
| + io_parent_, data, |
| + // push_registration_id_and_sender_id |
| + std::vector<std::string>(), |
| + SERVICE_WORKER_ERROR_NOT_FOUND)))); |
|
Peter Beverloo
2017/03/20 23:50:13
This is deep. Like, literally. Can we maybe make i
johnme
2017/03/30 18:36:38
Done.
|
| } |
| - |
| - SendSubscriptionSuccess(data, PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE, |
| - push_registration_id, p256dh, auth); |
| } |
| void PushMessagingMessageFilter::DidGetSenderIdFromStorage( |
| @@ -635,7 +682,8 @@ void PushMessagingMessageFilter::Core::UnregisterFromService( |
| } |
| push_service->Unsubscribe( |
| - requesting_origin, service_worker_registration_id, sender_id, |
| + PUSH_UNREGISTRATION_REASON_JAVASCRIPT_API, requesting_origin, |
| + service_worker_registration_id, sender_id, |
| base::Bind(&Core::DidUnregisterFromService, |
| weak_factory_ui_to_ui_.GetWeakPtr(), request_id, |
| service_worker_registration_id)); |
| @@ -709,6 +757,9 @@ void PushMessagingMessageFilter::DidGetSubscription( |
| switch (service_worker_status) { |
| case SERVICE_WORKER_OK: { |
| DCHECK_EQ(2u, push_subscription_id_and_sender_info.size()); |
| + const std::string& push_subscription_id = |
| + push_subscription_id_and_sender_info[0]; |
| + const std::string& sender_info = push_subscription_id_and_sender_info[1]; |
| if (!service_available_) { |
| // Return not found in incognito mode, so websites can't detect it. |
| @@ -724,22 +775,20 @@ void PushMessagingMessageFilter::DidGetSubscription( |
| service_worker_registration_id); |
| const GURL origin = registration->pattern().GetOrigin(); |
| - const bool uses_standard_protocol = |
| - IsApplicationServerKey(push_subscription_id_and_sender_info[1]); |
| - const GURL endpoint = CreateEndpoint( |
| - uses_standard_protocol, push_subscription_id_and_sender_info[0]); |
| - |
| - auto callback = |
| - base::Bind(&PushMessagingMessageFilter::DidGetSubscriptionKeys, |
| - weak_factory_io_to_io_.GetWeakPtr(), request_id, endpoint, |
| - push_subscription_id_and_sender_info[1]); |
| + const bool uses_standard_protocol = IsApplicationServerKey(sender_info); |
| + const GURL endpoint = |
| + CreateEndpoint(uses_standard_protocol, push_subscription_id); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| - base::Bind(&Core::GetEncryptionInfoOnUI, |
| + base::Bind(&Core::GetSubscriptionInfoOnUI, |
| base::Unretained(ui_core_.get()), origin, |
| - service_worker_registration_id, |
| - push_subscription_id_and_sender_info[1], callback)); |
| + service_worker_registration_id, sender_info, |
| + push_subscription_id, |
| + base::Bind(&Core::GetSubscriptionDidGetInfoOnUI, |
| + ui_core_weak_ptr_, request_id, origin, |
| + service_worker_registration_id, endpoint, |
| + sender_info))); |
| return; |
| } |
| @@ -778,35 +827,55 @@ void PushMessagingMessageFilter::DidGetSubscription( |
| RecordGetRegistrationStatus(get_status); |
| } |
| -void PushMessagingMessageFilter::DidGetSubscriptionKeys( |
| +void PushMessagingMessageFilter::Core::GetSubscriptionDidGetInfoOnUI( |
| int request_id, |
| + const GURL& origin, |
| + int64_t service_worker_registration_id, |
| const GURL& endpoint, |
| const std::string& sender_info, |
| - bool success, |
| + bool is_valid, |
| const std::vector<uint8_t>& p256dh, |
| const std::vector<uint8_t>& auth) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| - if (!success) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (is_valid) { |
| + PushSubscriptionOptions options; |
| + // Chrome rejects subscription requests with userVisibleOnly false, so it |
| + // must have been true. TODO(harkness): If Chrome starts accepting silent |
| + // push subscriptions with userVisibleOnly false, the bool will need to be |
| + // stored. |
| + options.user_visible_only = true; |
| + options.sender_info = sender_info; |
| + |
| + Send(new PushMessagingMsg_GetSubscriptionSuccess(request_id, endpoint, |
| + options, p256dh, auth)); |
| + |
| + RecordGetRegistrationStatus(PUSH_GETREGISTRATION_STATUS_SUCCESS); |
| + } else { |
| + // Uh-oh! Although there was a cached subscription in the Service Worker |
| + // database, it did not have matching counterparts in the |
| + // PushMessagingAppIdentifier map and/or GCM Store. Unsubscribe to fix this |
| + // inconsistency. |
| PushGetRegistrationStatus status = |
| - PUSH_GETREGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE; |
| + PUSH_GETREGISTRATION_STATUS_STORAGE_CORRUPT; |
| - Send(new PushMessagingMsg_GetSubscriptionError(request_id, status)); |
| + PushMessagingService* push_service = service(); |
| + DCHECK(push_service); // NOT_FOUND response can only come from service. |
| + push_service->Unsubscribe( |
| + PUSH_UNREGISTRATION_REASON_GET_SUBSCRIPTION_STORAGE_CORRUPT, origin, |
| + service_worker_registration_id, sender_info, |
| + base::Bind(&Core::GetSubscriptionDidUnsubscribe, |
| + weak_factory_ui_to_ui_.GetWeakPtr(), request_id, status)); |
| RecordGetRegistrationStatus(status); |
| - return; |
| } |
| +} |
| - PushSubscriptionOptions options; |
| - // Chrome rejects subscription requests with userVisibleOnly false, so it must |
| - // have been true. TODO(harkness): If Chrome starts accepting silent push |
| - // subscriptions with userVisibleOnly false, the bool will need to be stored. |
| - options.user_visible_only = true; |
| - options.sender_info = sender_info; |
| - |
| - Send(new PushMessagingMsg_GetSubscriptionSuccess(request_id, endpoint, |
| - options, p256dh, auth)); |
| - |
| - RecordGetRegistrationStatus(PUSH_GETREGISTRATION_STATUS_SUCCESS); |
| +void PushMessagingMessageFilter::Core::GetSubscriptionDidUnsubscribe( |
| + int request_id, |
| + PushGetRegistrationStatus get_status, |
| + PushUnregistrationStatus unsubscribe_status) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + Send(new PushMessagingMsg_GetSubscriptionError(request_id, get_status)); |
| } |
| // GetPermission methods on both IO and UI threads, merged in order of use from |
| @@ -864,24 +933,22 @@ void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI( |
| // PushMessagingMessageFilter and Core. |
| // ----------------------------------------------------------------------------- |
| -void PushMessagingMessageFilter::Core::GetEncryptionInfoOnUI( |
| +void PushMessagingMessageFilter::Core::GetSubscriptionInfoOnUI( |
| const GURL& origin, |
| int64_t service_worker_registration_id, |
| const std::string& sender_id, |
| - const PushMessagingService::EncryptionInfoCallback& io_thread_callback) { |
| + const std::string& push_subscription_id, |
| + const PushMessagingService::SubscriptionInfoCallback& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| PushMessagingService* push_service = service(); |
| - if (push_service) { |
| - push_service->GetEncryptionInfo( |
| - origin, service_worker_registration_id, sender_id, |
| - base::Bind(&ForwardEncryptionInfoToIOThreadProxy, io_thread_callback)); |
| + if (!push_service) { |
| + callback.Run(false /* is_valid */, std::vector<uint8_t>() /* p256dh */, |
| + std::vector<uint8_t>() /* auth */); |
| return; |
| } |
| - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
| - base::Bind(io_thread_callback, false /* success */, |
| - std::vector<uint8_t>() /* p256dh */, |
| - std::vector<uint8_t>() /* auth */)); |
| + push_service->GetSubscriptionInfo(origin, service_worker_registration_id, |
| + sender_id, push_subscription_id, callback); |
| } |
| void PushMessagingMessageFilter::Core::Send(IPC::Message* message) { |
| @@ -914,4 +981,10 @@ PushMessagingService* PushMessagingMessageFilter::Core::service() { |
| : nullptr; |
| } |
| +base::WeakPtr<PushMessagingMessageFilter::Core> |
| +PushMessagingMessageFilter::Core::GetWeakPtrFromIOParentConstructor() { |
|
Peter Beverloo
2017/03/20 23:50:13
Would you mind summarising in two sentences or so
johnme
2017/03/30 18:36:38
weak_factory_ui_to_ui_ should not be public, as it
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + return weak_factory_ui_to_ui_.GetWeakPtr(); |
| +} |
| + |
| } // namespace content |