1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/notifications/notification_ui_manager.h"
19 #include "chrome/browser/notifications/platform_notification_service_impl.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/services/gcm/gcm_profile_service.h"
22 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
23 #include "chrome/browser/services/gcm/push_messaging_application_id.h"
24 #include "chrome/browser/services/gcm/push_messaging_constants.h"
25 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
26 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "components/content_settings/core/browser/host_content_settings_map.h"
31 #include "components/content_settings/core/common/permission_request_id.h"
32 #include "components/gcm_driver/gcm_driver.h"
33 #include "components/pref_registry/pref_registry_syncable.h"
34 #include "content/public/browser/browser_context.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/render_frame_host.h"
37 #include "content/public/browser/service_worker_context.h"
38 #include "content/public/browser/storage_partition.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/common/child_process_host.h"
41 #include "content/public/common/content_switches.h"
42 #include "content/public/common/platform_notification_data.h"
43 #include "content/public/common/push_messaging_status.h"
44 #include "third_party/skia/include/core/SkBitmap.h"
45 #include "ui/base/l10n/l10n_util.h"
47 #if defined(OS_ANDROID)
48 #include "chrome/browser/ui/android/tab_model/tab_model.h"
49 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
51 #include "chrome/browser/ui/browser.h"
52 #include "chrome/browser/ui/browser_iterator.h"
53 #include "chrome/browser/ui/tabs/tab_strip_model.h"
59 const int kMaxRegistrations
= 1000000;
61 void RecordDeliveryStatus(content::PushDeliveryStatus status
) {
62 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
64 content::PUSH_DELIVERY_STATUS_LAST
+ 1);
67 void RecordUserVisibleStatus(content::PushUserVisibleStatus status
) {
68 UMA_HISTOGRAM_ENUMERATION("PushMessaging.UserVisibleStatus",
70 content::PUSH_USER_VISIBLE_STATUS_LAST
+ 1);
73 blink::WebPushPermissionStatus
ToPushPermission(ContentSetting setting
) {
75 case CONTENT_SETTING_ALLOW
:
76 return blink::WebPushPermissionStatusGranted
;
77 case CONTENT_SETTING_BLOCK
:
78 return blink::WebPushPermissionStatusDenied
;
79 case CONTENT_SETTING_ASK
:
80 return blink::WebPushPermissionStatusDefault
;
83 return blink::WebPushPermissionStatusDenied
;
90 void PushMessagingServiceImpl::RegisterProfilePrefs(
91 user_prefs::PrefRegistrySyncable
* registry
) {
92 registry
->RegisterIntegerPref(
93 prefs::kPushMessagingRegistrationCount
,
95 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
96 PushMessagingApplicationId::RegisterProfilePrefs(registry
);
100 void PushMessagingServiceImpl::InitializeForProfile(Profile
* profile
) {
101 // TODO(johnme): Consider whether push should be enabled in incognito.
102 if (!profile
|| profile
->IsOffTheRecord())
105 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
106 // pref), be careful that this pref is read from the right profile, as prefs
107 // defined in a regular profile are visible in the corresponding incognito
108 // profile unless overridden.
109 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
110 int count
= profile
->GetPrefs()->GetInteger(
111 prefs::kPushMessagingRegistrationCount
);
115 // Create the GCMProfileService, and hence instantiate this class.
116 GCMProfileService
* gcm_service
=
117 GCMProfileServiceFactory::GetForProfile(profile
);
118 PushMessagingServiceImpl
* push_service
=
119 static_cast<PushMessagingServiceImpl
*>(
120 gcm_service
->push_messaging_service());
122 push_service
->IncreasePushRegistrationCount(count
, false /* is_pending */);
125 PushMessagingServiceImpl::PushMessagingServiceImpl(
126 GCMProfileService
* gcm_profile_service
,
128 : gcm_profile_service_(gcm_profile_service
),
130 push_registration_count_(0),
131 pending_push_registration_count_(0),
132 weak_factory_(this) {
133 // In some tests, we might end up with |profile_| being null at this point.
134 // When that is the case |profile_| will be set in SetProfileForTesting(), at
135 // which point the service will start to observe HostContentSettingsMap.
137 profile_
->GetHostContentSettingsMap()->AddObserver(this);
140 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
141 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
142 // then we should call RemoveAppHandler.
143 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
146 void PushMessagingServiceImpl::IncreasePushRegistrationCount(int add
,
149 if (push_registration_count_
+ pending_push_registration_count_
== 0) {
150 gcm_profile_service_
->driver()->AddAppHandler(
151 kPushMessagingApplicationIdPrefix
, this);
154 pending_push_registration_count_
+= add
;
156 push_registration_count_
+= add
;
157 profile_
->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount
,
158 push_registration_count_
);
162 void PushMessagingServiceImpl::DecreasePushRegistrationCount(int subtract
,
164 DCHECK(subtract
> 0);
166 pending_push_registration_count_
-= subtract
;
167 DCHECK(pending_push_registration_count_
>= 0);
169 push_registration_count_
-= subtract
;
170 DCHECK(push_registration_count_
>= 0);
171 profile_
->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount
,
172 push_registration_count_
);
174 if (push_registration_count_
+ pending_push_registration_count_
== 0) {
175 gcm_profile_service_
->driver()->RemoveAppHandler(
176 kPushMessagingApplicationIdPrefix
);
180 bool PushMessagingServiceImpl::CanHandle(const std::string
& app_id
) const {
181 return PushMessagingApplicationId::Get(profile_
, app_id
).IsValid();
184 void PushMessagingServiceImpl::ShutdownHandler() {
185 // TODO(johnme): Do any necessary cleanup.
188 // OnMessage methods -----------------------------------------------------------
190 void PushMessagingServiceImpl::OnMessage(
191 const std::string
& app_id
,
192 const GCMClient::IncomingMessage
& message
) {
193 PushMessagingApplicationId application_id
=
194 PushMessagingApplicationId::Get(profile_
, app_id
);
195 // Drop message and unregister if app id was unknown (maybe recently deleted).
196 if (!application_id
.IsValid()) {
197 DeliverMessageCallback(app_id
, GURL::EmptyGURL(), -1, message
,
198 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID
);
201 // Drop message and unregister if |origin| has lost push permission.
202 if (!HasPermission(application_id
.origin())) {
203 DeliverMessageCallback(app_id
, application_id
.origin(),
204 application_id
.service_worker_registration_id(),
206 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED
);
210 // The Push API only exposes a single string of data in the push event fired
211 // on the Service Worker. When developers send messages using GCM to the Push
212 // API and want to include a message payload, they must pass a single key-
213 // value pair, where the key is "data" and the value is the string they want
214 // to be passed to their Service Worker. For example, they could send the
215 // following JSON using the HTTPS GCM API:
217 // "registration_ids": ["FOO", "BAR"],
221 // "delay_while_idle": true,
223 // TODO(johnme): Make sure this is clearly documented for developers.
225 // TODO(peter): Message payloads are disabled pending mandatory encryption.
226 // https://crbug.com/449184
227 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
228 switches::kEnablePushMessagePayload
)) {
229 GCMClient::MessageData::const_iterator it
= message
.data
.find("data");
230 if (it
!= message
.data
.end())
234 content::BrowserContext::DeliverPushMessage(
236 application_id
.origin(),
237 application_id
.service_worker_registration_id(),
239 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback
,
240 weak_factory_
.GetWeakPtr(),
241 application_id
.app_id_guid(), application_id
.origin(),
242 application_id
.service_worker_registration_id(), message
));
245 void PushMessagingServiceImpl::DeliverMessageCallback(
246 const std::string
& app_id_guid
,
247 const GURL
& requesting_origin
,
248 int64 service_worker_registration_id
,
249 const GCMClient::IncomingMessage
& message
,
250 content::PushDeliveryStatus status
) {
251 // TODO(mvanouwerkerk): Show a warning in the developer console of the
252 // Service Worker corresponding to app_id (and/or on an internals page).
253 // TODO(mvanouwerkerk): Is there a way to recover from failure?
255 // Call RequireUserVisibleUX if the message was delivered to the Service
256 // Worker JS, even if the website's event handler failed (to prevent sites
257 // deliberately failing in order to avoid having to show notifications).
258 case content::PUSH_DELIVERY_STATUS_SUCCESS
:
259 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED
:
260 RequireUserVisibleUX(requesting_origin
, service_worker_registration_id
);
262 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE
:
263 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR
:
265 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID
:
266 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED
:
267 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER
:
268 Unregister(app_id_guid
, message
.sender_id
, true /* retry_on_failure */,
269 UnregisterCallback());
272 RecordDeliveryStatus(status
);
275 void PushMessagingServiceImpl::RequireUserVisibleUX(
276 const GURL
& requesting_origin
, int64 service_worker_registration_id
) {
277 #if defined(ENABLE_NOTIFICATIONS)
278 // TODO(johnme): Relax this heuristic slightly.
279 PlatformNotificationServiceImpl
* notification_service
=
280 PlatformNotificationServiceImpl::GetInstance();
281 // Can't use g_browser_process->notification_ui_manager(), since the test uses
282 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting.
283 // TODO(peter): Remove the need to use both APIs here once Notification.get()
285 int notification_count
= notification_service
->GetNotificationUIManager()->
286 GetAllIdsByProfileAndSourceOrigin(profile_
, requesting_origin
).size();
287 // TODO(johnme): Hiding an existing notification should also count as a useful
288 // user-visible action done in response to a push message - but make sure that
289 // sending two messages in rapid succession which show then hide a
290 // notification doesn't count.
291 bool notification_shown
= notification_count
> 0;
293 bool notification_needed
= true;
294 // Sites with a currently visible tab don't need to show notifications.
295 #if defined(OS_ANDROID)
296 for (auto it
= TabModelList::begin(); it
!= TabModelList::end(); ++it
) {
297 Profile
* profile
= (*it
)->GetProfile();
298 content::WebContents
* active_web_contents
=
299 (*it
)->GetActiveWebContents();
301 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
302 Profile
* profile
= it
->profile();
303 content::WebContents
* active_web_contents
=
304 it
->tab_strip_model()->GetActiveWebContents();
306 if (!active_web_contents
|| !active_web_contents
->GetMainFrame())
309 // Don't leak information from other profiles.
310 if (profile
!= profile_
)
313 // Ignore minimized windows etc.
314 switch (active_web_contents
->GetMainFrame()->GetVisibilityState()) {
315 case blink::WebPageVisibilityStateHidden
:
316 case blink::WebPageVisibilityStatePrerender
:
318 case blink::WebPageVisibilityStateVisible
:
322 // Use the visible URL since that's the one the user is aware of (and it
323 // doesn't matter whether the page loaded successfully).
324 const GURL
& active_url
= active_web_contents
->GetVisibleURL();
325 if (requesting_origin
== active_url
.GetOrigin()) {
326 notification_needed
= false;
329 #if defined(OS_ANDROID)
335 // Don't track push messages that didn't show a notification but were exempt
336 // from needing to do so.
337 if (notification_shown
|| notification_needed
) {
338 content::ServiceWorkerContext
* service_worker_context
=
339 content::BrowserContext::GetStoragePartitionForSite(
340 profile_
, requesting_origin
)->GetServiceWorkerContext();
342 PushMessagingService::GetNotificationsShownByLastFewPushes(
343 service_worker_context
, service_worker_registration_id
,
344 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown
,
345 weak_factory_
.GetWeakPtr(),
346 requesting_origin
, service_worker_registration_id
,
347 notification_shown
, notification_needed
));
349 RecordUserVisibleStatus(
350 content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_AND_NOT_SHOWN
);
352 #endif // defined(ENABLE_NOTIFICATIONS)
355 static void IgnoreResult(bool unused
) {
358 void PushMessagingServiceImpl::DidGetNotificationsShown(
359 const GURL
& requesting_origin
, int64 service_worker_registration_id
,
360 bool notification_shown
, bool notification_needed
,
361 const std::string
& data
, bool success
, bool not_found
) {
362 content::ServiceWorkerContext
* service_worker_context
=
363 content::BrowserContext::GetStoragePartitionForSite(
364 profile_
, requesting_origin
)->GetServiceWorkerContext();
366 // We remember whether the last (up to) 10 pushes showed notifications.
367 const size_t MISSED_NOTIFICATIONS_LENGTH
= 10;
368 // data is a string like "0001000", where '0' means shown, and '1' means
369 // needed but not shown. We manipulate it in bitset form.
370 std::bitset
<MISSED_NOTIFICATIONS_LENGTH
> missed_notifications(data
);
372 bool needed_but_not_shown
= notification_needed
&& !notification_shown
;
374 // New entries go at the end, and old ones are shifted off the beginning once
375 // the history length is exceeded.
376 missed_notifications
<<= 1;
377 missed_notifications
[0] = needed_but_not_shown
;
378 std::string
updated_data(missed_notifications
.
379 to_string
<char, std::string::traits_type
, std::string::allocator_type
>());
380 PushMessagingService::SetNotificationsShownByLastFewPushes(
381 service_worker_context
, service_worker_registration_id
,
382 requesting_origin
, updated_data
,
383 base::Bind(&IgnoreResult
)); // This is a heuristic; ignore failure.
385 if (notification_shown
) {
386 RecordUserVisibleStatus(
388 ? content::PUSH_USER_VISIBLE_STATUS_REQUIRED_AND_SHOWN
389 : content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_BUT_SHOWN
);
392 if (needed_but_not_shown
) {
393 if (missed_notifications
.count() <= 1) {
394 RecordUserVisibleStatus(
395 content::PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_USED_GRACE
);
398 RecordUserVisibleStatus(
400 PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED
);
401 // The site failed to show a notification when one was needed, and they have
402 // already failed once in the previous 10 push messages, so we will show a
403 // generic notification. See https://crbug.com/437277.
404 // TODO(johnme): The generic notification should probably automatically
405 // close itself when the next push message arrives?
406 content::PlatformNotificationData notification_data
;
407 // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698
408 notification_data
.title
= base::UTF8ToUTF16(requesting_origin
.host());
409 notification_data
.direction
=
410 content::PlatformNotificationData::NotificationDirectionLeftToRight
;
411 notification_data
.body
=
412 l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY
);
413 notification_data
.tag
=
414 base::ASCIIToUTF16(kPushMessagingForcedNotificationTag
);
415 notification_data
.icon
= GURL(); // TODO(johnme): Better icon?
416 PlatformNotificationServiceImpl
* notification_service
=
417 PlatformNotificationServiceImpl::GetInstance();
418 notification_service
->DisplayPersistentNotification(
420 service_worker_registration_id
,
422 SkBitmap() /* icon */,
427 // Other GCMAppHandler methods -------------------------------------------------
429 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string
& app_id
) {
430 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
431 // corresponding to app_id.
434 void PushMessagingServiceImpl::OnSendError(
435 const std::string
& app_id
,
436 const GCMClient::SendErrorDetails
& send_error_details
) {
437 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
440 void PushMessagingServiceImpl::OnSendAcknowledged(
441 const std::string
& app_id
,
442 const std::string
& message_id
) {
443 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
446 // GetPushEndpoint method ------------------------------------------------------
448 GURL
PushMessagingServiceImpl::GetPushEndpoint() {
449 return GURL(std::string(kPushMessagingEndpoint
));
452 // Register and GetPermissionStatus methods ------------------------------------
454 void PushMessagingServiceImpl::RegisterFromDocument(
455 const GURL
& requesting_origin
,
456 int64 service_worker_registration_id
,
457 const std::string
& sender_id
,
460 bool user_visible_only
,
461 const content::PushMessagingService::RegisterCallback
& callback
) {
462 if (!gcm_profile_service_
->driver()) {
463 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
467 PushMessagingApplicationId application_id
=
468 PushMessagingApplicationId::Generate(requesting_origin
,
469 service_worker_registration_id
);
470 DCHECK(application_id
.IsValid());
472 if (push_registration_count_
+ pending_push_registration_count_
473 >= kMaxRegistrations
) {
474 RegisterEnd(callback
,
476 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED
);
480 content::RenderFrameHost
* render_frame_host
=
481 content::RenderFrameHost::FromID(renderer_id
, render_frame_id
);
482 if (!render_frame_host
)
485 content::WebContents
* web_contents
=
486 content::WebContents::FromRenderFrameHost(render_frame_host
);
490 // TODO(miguelg) need to send this over IPC when bubble support is
494 const PermissionRequestID
id(
495 renderer_id
, web_contents
->GetRoutingID(), bridge_id
, GURL());
497 gcm::PushMessagingPermissionContext
* permission_context
=
498 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_
);
500 if (permission_context
== NULL
|| !user_visible_only
) {
501 RegisterEnd(callback
,
503 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
507 // TODO(miguelg): Consider the value of |user_visible_only| when making
508 // the permission request.
509 // TODO(mlamouri): Move requesting Push permission over to using Mojo, and
510 // re-introduce the ability of |user_gesture| when bubbles require this.
511 // https://crbug.com/423770.
512 permission_context
->RequestPermission(
513 web_contents
, id
, requesting_origin
, true /* user_gesture */,
514 base::Bind(&PushMessagingServiceImpl::DidRequestPermission
,
515 weak_factory_
.GetWeakPtr(), application_id
, sender_id
,
519 void PushMessagingServiceImpl::RegisterFromWorker(
520 const GURL
& requesting_origin
,
521 int64 service_worker_registration_id
,
522 const std::string
& sender_id
,
523 const content::PushMessagingService::RegisterCallback
& register_callback
) {
524 if (!gcm_profile_service_
->driver()) {
525 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
529 PushMessagingApplicationId application_id
=
530 PushMessagingApplicationId::Generate(requesting_origin
,
531 service_worker_registration_id
);
532 DCHECK(application_id
.IsValid());
534 if (profile_
->GetPrefs()->GetInteger(
535 prefs::kPushMessagingRegistrationCount
) >= kMaxRegistrations
) {
536 RegisterEnd(register_callback
, std::string(),
537 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED
);
541 GURL embedding_origin
= requesting_origin
;
542 blink::WebPushPermissionStatus permission_status
=
543 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin
,
545 if (permission_status
!= blink::WebPushPermissionStatusGranted
) {
546 RegisterEnd(register_callback
, std::string(),
547 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
551 IncreasePushRegistrationCount(1, true /* is_pending */);
552 std::vector
<std::string
> sender_ids(1, sender_id
);
553 gcm_profile_service_
->driver()->Register(
554 application_id
.app_id_guid(), sender_ids
,
555 base::Bind(&PushMessagingServiceImpl::DidRegister
,
556 weak_factory_
.GetWeakPtr(),
557 application_id
, register_callback
));
560 blink::WebPushPermissionStatus
PushMessagingServiceImpl::GetPermissionStatus(
561 const GURL
& requesting_origin
,
562 const GURL
& embedding_origin
) {
563 PushMessagingPermissionContext
* permission_context
=
564 PushMessagingPermissionContextFactory::GetForProfile(profile_
);
565 return ToPushPermission(permission_context
->GetPermissionStatus(
566 requesting_origin
, embedding_origin
));
569 void PushMessagingServiceImpl::RegisterEnd(
570 const content::PushMessagingService::RegisterCallback
& callback
,
571 const std::string
& registration_id
,
572 content::PushRegistrationStatus status
) {
573 callback
.Run(registration_id
, status
);
576 void PushMessagingServiceImpl::DidRegister(
577 const PushMessagingApplicationId
& application_id
,
578 const content::PushMessagingService::RegisterCallback
& callback
,
579 const std::string
& registration_id
,
580 GCMClient::Result result
) {
581 content::PushRegistrationStatus status
=
582 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR
;
584 case GCMClient::SUCCESS
:
585 status
= content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE
;
586 application_id
.PersistToDisk(profile_
);
587 IncreasePushRegistrationCount(1, false /* is_pending */);
589 case GCMClient::INVALID_PARAMETER
:
590 case GCMClient::GCM_DISABLED
:
591 case GCMClient::ASYNC_OPERATION_PENDING
:
592 case GCMClient::SERVER_ERROR
:
593 case GCMClient::UNKNOWN_ERROR
:
594 status
= content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR
;
596 case GCMClient::NETWORK_ERROR
:
597 case GCMClient::TTL_EXCEEDED
:
598 status
= content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR
;
601 RegisterEnd(callback
, registration_id
, status
);
602 DecreasePushRegistrationCount(1, true /* was_pending */);
605 void PushMessagingServiceImpl::DidRequestPermission(
606 const PushMessagingApplicationId
& application_id
,
607 const std::string
& sender_id
,
608 const content::PushMessagingService::RegisterCallback
& register_callback
,
609 ContentSetting content_setting
) {
610 if (content_setting
!= CONTENT_SETTING_ALLOW
) {
611 RegisterEnd(register_callback
,
613 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
617 // The GCMDriver could be NULL if GCMProfileService has been shut down.
618 if (!gcm_profile_service_
->driver())
621 IncreasePushRegistrationCount(1, true /* is_pending */);
622 std::vector
<std::string
> sender_ids(1, sender_id
);
623 gcm_profile_service_
->driver()->Register(
624 application_id
.app_id_guid(),
626 base::Bind(&PushMessagingServiceImpl::DidRegister
,
627 weak_factory_
.GetWeakPtr(),
628 application_id
, register_callback
));
631 // Unregister methods ----------------------------------------------------------
633 void PushMessagingServiceImpl::Unregister(
634 const GURL
& requesting_origin
,
635 int64 service_worker_registration_id
,
636 const std::string
& sender_id
,
637 bool retry_on_failure
,
638 const content::PushMessagingService::UnregisterCallback
& callback
) {
639 DCHECK(gcm_profile_service_
->driver());
641 PushMessagingApplicationId application_id
= PushMessagingApplicationId::Get(
642 profile_
, requesting_origin
, service_worker_registration_id
);
643 if (!application_id
.IsValid()) {
644 if (!callback
.is_null()) {
646 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED
);
651 Unregister(application_id
.app_id_guid(), sender_id
, retry_on_failure
,
655 void PushMessagingServiceImpl::Unregister(
656 const std::string
& app_id_guid
,
657 const std::string
& sender_id
,
658 bool retry_on_failure
,
659 const content::PushMessagingService::UnregisterCallback
& callback
) {
660 DCHECK(gcm_profile_service_
->driver());
662 if (retry_on_failure
) {
663 // Delete the mapping for this app id, to guarantee that no messages get
664 // delivered in future (even if unregistration fails).
665 // TODO(johnme): Instead of deleting these app ids, store them elsewhere,
666 // and retry unregistration if it fails due to network errors.
667 PushMessagingApplicationId application_id
=
668 PushMessagingApplicationId::Get(profile_
, app_id_guid
);
669 if (application_id
.IsValid())
670 application_id
.DeleteFromDisk(profile_
);
673 const auto& unregister_callback
=
674 base::Bind(&PushMessagingServiceImpl::DidUnregister
,
675 weak_factory_
.GetWeakPtr(),
676 app_id_guid
, retry_on_failure
, callback
);
677 #if defined(OS_ANDROID)
678 // On Android the backend is different, and requires the original sender_id.
679 gcm_profile_service_
->driver()->UnregisterWithSenderId(app_id_guid
, sender_id
,
680 unregister_callback
);
682 gcm_profile_service_
->driver()->Unregister(app_id_guid
, unregister_callback
);
686 void PushMessagingServiceImpl::DidUnregister(
687 const std::string
& app_id_guid
,
688 bool retry_on_failure
,
689 const content::PushMessagingService::UnregisterCallback
& callback
,
690 GCMClient::Result result
) {
691 if (result
== GCMClient::SUCCESS
) {
692 PushMessagingApplicationId application_id
=
693 PushMessagingApplicationId::Get(profile_
, app_id_guid
);
694 if (!application_id
.IsValid()) {
695 if (!callback
.is_null()) {
697 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED
);
702 application_id
.DeleteFromDisk(profile_
);
703 DecreasePushRegistrationCount(1, false /* was_pending */);
706 // Internal calls pass a null callback.
707 if (!callback
.is_null()) {
709 case GCMClient::SUCCESS
:
710 callback
.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED
);
712 case GCMClient::INVALID_PARAMETER
:
713 case GCMClient::GCM_DISABLED
:
714 case GCMClient::ASYNC_OPERATION_PENDING
:
715 case GCMClient::SERVER_ERROR
:
716 case GCMClient::UNKNOWN_ERROR
:
717 callback
.Run(content::PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR
);
719 case GCMClient::NETWORK_ERROR
:
720 case GCMClient::TTL_EXCEEDED
:
724 PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR
725 : content::PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR
);
731 // OnContentSettingChanged methods ---------------------------------------------
733 void PushMessagingServiceImpl::OnContentSettingChanged(
734 const ContentSettingsPattern
& primary_pattern
,
735 const ContentSettingsPattern
& secondary_pattern
,
736 ContentSettingsType content_type
,
737 std::string resource_identifier
) {
738 if (content_type
!= CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
&&
739 content_type
!= CONTENT_SETTINGS_TYPE_NOTIFICATIONS
) {
743 for (const auto& id
: PushMessagingApplicationId::GetAll(profile_
)) {
744 // If |primary_pattern| is not valid, we should always check for a
745 // permission change because it can happen for example when the entire
746 // Push or Notifications permissions are cleared.
747 // Otherwise, the permission should be checked if the pattern matches the
749 if (primary_pattern
.IsValid() && !primary_pattern
.Matches(id
.origin()))
752 if (HasPermission(id
.origin()))
755 PushMessagingService::GetSenderId(
756 profile_
, id
.origin(), id
.service_worker_registration_id(),
758 &PushMessagingServiceImpl::UnregisterBecausePermissionRevoked
,
759 weak_factory_
.GetWeakPtr(), id
));
763 void PushMessagingServiceImpl::UnregisterBecausePermissionRevoked(
764 const PushMessagingApplicationId
& id
,
765 const std::string
& sender_id
, bool success
, bool not_found
) {
766 // Unregister the PushMessagingApplicationId with the push service.
767 Unregister(id
.app_id_guid(), sender_id
, true /* retry_on_failure */,
768 UnregisterCallback());
770 // Clear the associated service worker push registration id.
771 PushMessagingService::ClearPushRegistrationID(
772 profile_
, id
.origin(), id
.service_worker_registration_id());
775 // Helper methods --------------------------------------------------------------
777 bool PushMessagingServiceImpl::HasPermission(const GURL
& origin
) {
778 gcm::PushMessagingPermissionContext
* permission_context
=
779 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_
);
780 DCHECK(permission_context
);
782 return permission_context
->GetPermissionStatus(origin
, origin
) ==
783 CONTENT_SETTING_ALLOW
;
786 void PushMessagingServiceImpl::SetProfileForTesting(Profile
* profile
) {
788 profile_
->GetHostContentSettingsMap()->AddObserver(this);