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/push_messaging/push_messaging_service_impl.h"
10 #include "base/barrier_closure.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/notifications/notification_ui_manager.h"
21 #include "chrome/browser/notifications/platform_notification_service_impl.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/push_messaging/push_messaging_application_id.h"
24 #include "chrome/browser/push_messaging/push_messaging_constants.h"
25 #include "chrome/browser/push_messaging/push_messaging_permission_context.h"
26 #include "chrome/browser/push_messaging/push_messaging_permission_context_factory.h"
27 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
28 #include "chrome/browser/services/gcm/gcm_profile_service.h"
29 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/pref_names.h"
32 #include "chrome/grit/generated_resources.h"
33 #include "components/content_settings/core/browser/host_content_settings_map.h"
34 #include "components/content_settings/core/common/permission_request_id.h"
35 #include "components/gcm_driver/gcm_driver.h"
36 #include "components/pref_registry/pref_registry_syncable.h"
37 #include "components/rappor/rappor_utils.h"
38 #include "content/public/browser/browser_context.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/service_worker_context.h"
42 #include "content/public/browser/storage_partition.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/common/child_process_host.h"
45 #include "content/public/common/content_switches.h"
46 #include "content/public/common/platform_notification_data.h"
47 #include "content/public/common/push_messaging_status.h"
48 #include "third_party/skia/include/core/SkBitmap.h"
49 #include "ui/base/l10n/l10n_util.h"
51 #if defined(OS_ANDROID)
52 #include "chrome/browser/ui/android/tab_model/tab_model.h"
53 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
55 #include "chrome/browser/ui/browser.h"
56 #include "chrome/browser/ui/browser_iterator.h"
57 #include "chrome/browser/ui/tabs/tab_strip_model.h"
61 const int kMaxRegistrations
= 1000000;
63 void RecordDeliveryStatus(content::PushDeliveryStatus status
) {
64 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
66 content::PUSH_DELIVERY_STATUS_LAST
+ 1);
69 void RecordUserVisibleStatus(content::PushUserVisibleStatus status
) {
70 UMA_HISTOGRAM_ENUMERATION("PushMessaging.UserVisibleStatus",
72 content::PUSH_USER_VISIBLE_STATUS_LAST
+ 1);
75 blink::WebPushPermissionStatus
ToPushPermission(ContentSetting setting
) {
77 case CONTENT_SETTING_ALLOW
:
78 return blink::WebPushPermissionStatusGranted
;
79 case CONTENT_SETTING_BLOCK
:
80 return blink::WebPushPermissionStatusDenied
;
81 case CONTENT_SETTING_ASK
:
82 return blink::WebPushPermissionStatusDefault
;
85 return blink::WebPushPermissionStatusDenied
;
89 void UnregisterCallbackToClosure(
90 const base::Closure
& closure
, content::PushUnregistrationStatus status
) {
97 void PushMessagingServiceImpl::RegisterProfilePrefs(
98 user_prefs::PrefRegistrySyncable
* registry
) {
99 registry
->RegisterIntegerPref(
100 prefs::kPushMessagingRegistrationCount
,
102 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
103 PushMessagingApplicationId::RegisterProfilePrefs(registry
);
107 void PushMessagingServiceImpl::InitializeForProfile(Profile
* profile
) {
108 // TODO(johnme): Consider whether push should be enabled in incognito.
109 if (!profile
|| profile
->IsOffTheRecord())
112 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
113 // pref), be careful that this pref is read from the right profile, as prefs
114 // defined in a regular profile are visible in the corresponding incognito
115 // profile unless overridden.
116 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
117 int count
= profile
->GetPrefs()->GetInteger(
118 prefs::kPushMessagingRegistrationCount
);
122 PushMessagingServiceImpl
* push_service
=
123 PushMessagingServiceFactory::GetForProfile(profile
);
124 push_service
->IncreasePushRegistrationCount(count
, false /* is_pending */);
127 PushMessagingServiceImpl::PushMessagingServiceImpl(Profile
* profile
)
129 push_registration_count_(0),
130 pending_push_registration_count_(0),
131 weak_factory_(this) {
133 profile_
->GetHostContentSettingsMap()->AddObserver(this);
136 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
137 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
140 void PushMessagingServiceImpl::IncreasePushRegistrationCount(int add
,
143 if (push_registration_count_
+ pending_push_registration_count_
== 0) {
144 GetGCMDriver()->AddAppHandler(kPushMessagingApplicationIdPrefix
, this);
147 pending_push_registration_count_
+= add
;
149 push_registration_count_
+= add
;
150 profile_
->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount
,
151 push_registration_count_
);
155 void PushMessagingServiceImpl::DecreasePushRegistrationCount(int subtract
,
157 DCHECK(subtract
> 0);
159 pending_push_registration_count_
-= subtract
;
160 DCHECK(pending_push_registration_count_
>= 0);
162 push_registration_count_
-= subtract
;
163 DCHECK(push_registration_count_
>= 0);
164 profile_
->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount
,
165 push_registration_count_
);
167 if (push_registration_count_
+ pending_push_registration_count_
== 0) {
168 GetGCMDriver()->RemoveAppHandler(kPushMessagingApplicationIdPrefix
);
172 bool PushMessagingServiceImpl::CanHandle(const std::string
& app_id
) const {
173 return PushMessagingApplicationId::Get(profile_
, app_id
).IsValid();
176 void PushMessagingServiceImpl::ShutdownHandler() {
177 // Shutdown() should come before and it removes us from the list of app
178 // handlers of gcm::GCMDriver so this shouldn't ever been called.
182 // OnMessage methods -----------------------------------------------------------
184 void PushMessagingServiceImpl::OnMessage(
185 const std::string
& app_id
,
186 const gcm::GCMClient::IncomingMessage
& message
) {
187 PushMessagingApplicationId application_id
=
188 PushMessagingApplicationId::Get(profile_
, app_id
);
189 // Drop message and unregister if app id was unknown (maybe recently deleted).
190 if (!application_id
.IsValid()) {
191 DeliverMessageCallback(app_id
, GURL::EmptyGURL(), -1, message
,
192 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID
);
195 // Drop message and unregister if |origin| has lost push permission.
196 if (!HasPermission(application_id
.origin())) {
197 DeliverMessageCallback(app_id
, application_id
.origin(),
198 application_id
.service_worker_registration_id(),
200 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED
);
204 rappor::SampleDomainAndRegistryFromGURL(
205 g_browser_process
->rappor_service(),
206 "PushMessaging.MessageReceived.Origin",
207 application_id
.origin());
209 // The Push API only exposes a single string of data in the push event fired
210 // on the Service Worker. When developers send messages using GCM to the Push
211 // API and want to include a message payload, they must pass a single key-
212 // value pair, where the key is "data" and the value is the string they want
213 // to be passed to their Service Worker. For example, they could send the
214 // following JSON using the HTTPS GCM API:
216 // "registration_ids": ["FOO", "BAR"],
220 // "delay_while_idle": true,
222 // TODO(johnme): Make sure this is clearly documented for developers.
224 // TODO(peter): Message payloads are disabled pending mandatory encryption.
225 // https://crbug.com/449184
226 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
227 switches::kEnablePushMessagePayload
)) {
228 gcm::GCMClient::MessageData::const_iterator it
= message
.data
.find("data");
229 if (it
!= message
.data
.end())
233 content::BrowserContext::DeliverPushMessage(
235 application_id
.origin(),
236 application_id
.service_worker_registration_id(),
238 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback
,
239 weak_factory_
.GetWeakPtr(),
240 application_id
.app_id_guid(), application_id
.origin(),
241 application_id
.service_worker_registration_id(), message
));
244 void PushMessagingServiceImpl::DeliverMessageCallback(
245 const std::string
& app_id_guid
,
246 const GURL
& requesting_origin
,
247 int64 service_worker_registration_id
,
248 const gcm::GCMClient::IncomingMessage
& message
,
249 content::PushDeliveryStatus status
) {
250 // TODO(mvanouwerkerk): Show a warning in the developer console of the
251 // Service Worker corresponding to app_id (and/or on an internals page).
252 // TODO(mvanouwerkerk): Is there a way to recover from failure?
254 // Call RequireUserVisibleUX if the message was delivered to the Service
255 // Worker JS, even if the website's event handler failed (to prevent sites
256 // deliberately failing in order to avoid having to show notifications).
257 case content::PUSH_DELIVERY_STATUS_SUCCESS
:
258 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED
:
259 RequireUserVisibleUX(requesting_origin
, service_worker_registration_id
);
261 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE
:
262 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR
:
264 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID
:
265 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED
:
266 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER
:
267 Unregister(app_id_guid
, message
.sender_id
, UnregisterCallback());
270 RecordDeliveryStatus(status
);
273 void PushMessagingServiceImpl::RequireUserVisibleUX(
274 const GURL
& requesting_origin
, int64 service_worker_registration_id
) {
275 #if defined(ENABLE_NOTIFICATIONS)
276 // TODO(johnme): Relax this heuristic slightly.
277 PlatformNotificationServiceImpl
* notification_service
=
278 PlatformNotificationServiceImpl::GetInstance();
279 // Can't use g_browser_process->notification_ui_manager(), since the test uses
280 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting.
281 // TODO(peter): Remove the need to use both APIs here once Notification.get()
283 int notification_count
= notification_service
->GetNotificationUIManager()->
284 GetAllIdsByProfileAndSourceOrigin(profile_
, requesting_origin
).size();
285 // TODO(johnme): Hiding an existing notification should also count as a useful
286 // user-visible action done in response to a push message - but make sure that
287 // sending two messages in rapid succession which show then hide a
288 // notification doesn't count.
289 bool notification_shown
= notification_count
> 0;
291 bool notification_needed
= true;
292 // Sites with a currently visible tab don't need to show notifications.
293 #if defined(OS_ANDROID)
294 for (auto it
= TabModelList::begin(); it
!= TabModelList::end(); ++it
) {
295 Profile
* profile
= (*it
)->GetProfile();
296 content::WebContents
* active_web_contents
=
297 (*it
)->GetActiveWebContents();
299 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
300 Profile
* profile
= it
->profile();
301 content::WebContents
* active_web_contents
=
302 it
->tab_strip_model()->GetActiveWebContents();
304 if (!active_web_contents
|| !active_web_contents
->GetMainFrame())
307 // Don't leak information from other profiles.
308 if (profile
!= profile_
)
311 // Ignore minimized windows etc.
312 switch (active_web_contents
->GetMainFrame()->GetVisibilityState()) {
313 case blink::WebPageVisibilityStateHidden
:
314 case blink::WebPageVisibilityStatePrerender
:
316 case blink::WebPageVisibilityStateVisible
:
320 // Use the visible URL since that's the one the user is aware of (and it
321 // doesn't matter whether the page loaded successfully).
322 const GURL
& active_url
= active_web_contents
->GetVisibleURL();
323 if (requesting_origin
== active_url
.GetOrigin()) {
324 notification_needed
= false;
327 #if defined(OS_ANDROID)
333 // Don't track push messages that didn't show a notification but were exempt
334 // from needing to do so.
335 if (notification_shown
|| notification_needed
) {
336 content::ServiceWorkerContext
* service_worker_context
=
337 content::BrowserContext::GetStoragePartitionForSite(
338 profile_
, requesting_origin
)->GetServiceWorkerContext();
340 GetNotificationsShownByLastFewPushes(
341 service_worker_context
, service_worker_registration_id
,
342 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown
,
343 weak_factory_
.GetWeakPtr(),
344 requesting_origin
, service_worker_registration_id
,
345 notification_shown
, notification_needed
));
347 RecordUserVisibleStatus(
348 content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_AND_NOT_SHOWN
);
350 #endif // defined(ENABLE_NOTIFICATIONS)
353 static void IgnoreResult(bool unused
) {
356 void PushMessagingServiceImpl::DidGetNotificationsShown(
357 const GURL
& requesting_origin
, int64 service_worker_registration_id
,
358 bool notification_shown
, bool notification_needed
,
359 const std::string
& data
, bool success
, bool not_found
) {
360 content::ServiceWorkerContext
* service_worker_context
=
361 content::BrowserContext::GetStoragePartitionForSite(
362 profile_
, requesting_origin
)->GetServiceWorkerContext();
364 // We remember whether the last (up to) 10 pushes showed notifications.
365 const size_t MISSED_NOTIFICATIONS_LENGTH
= 10;
366 // data is a string like "0001000", where '0' means shown, and '1' means
367 // needed but not shown. We manipulate it in bitset form.
368 std::bitset
<MISSED_NOTIFICATIONS_LENGTH
> missed_notifications(data
);
370 bool needed_but_not_shown
= notification_needed
&& !notification_shown
;
372 // New entries go at the end, and old ones are shifted off the beginning once
373 // the history length is exceeded.
374 missed_notifications
<<= 1;
375 missed_notifications
[0] = needed_but_not_shown
;
376 std::string
updated_data(missed_notifications
.
377 to_string
<char, std::string::traits_type
, std::string::allocator_type
>());
378 SetNotificationsShownByLastFewPushes(
379 service_worker_context
, service_worker_registration_id
,
380 requesting_origin
, updated_data
,
381 base::Bind(&IgnoreResult
)); // This is a heuristic; ignore failure.
383 if (notification_shown
) {
384 RecordUserVisibleStatus(
386 ? content::PUSH_USER_VISIBLE_STATUS_REQUIRED_AND_SHOWN
387 : content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_BUT_SHOWN
);
390 if (needed_but_not_shown
) {
391 if (missed_notifications
.count() <= 1) { // apply grace
392 RecordUserVisibleStatus(
393 content::PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_USED_GRACE
);
396 RecordUserVisibleStatus(
398 PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED
);
399 rappor::SampleDomainAndRegistryFromGURL(
400 g_browser_process
->rappor_service(),
401 "PushMessaging.GenericNotificationShown.Origin",
403 // The site failed to show a notification when one was needed, and they have
404 // already failed once in the previous 10 push messages, so we will show a
405 // generic notification. See https://crbug.com/437277.
406 // TODO(johnme): The generic notification should probably automatically
407 // close itself when the next push message arrives?
408 content::PlatformNotificationData notification_data
;
409 // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698
410 notification_data
.title
= base::UTF8ToUTF16(requesting_origin
.host());
411 notification_data
.direction
=
412 content::PlatformNotificationData::NotificationDirectionLeftToRight
;
413 notification_data
.body
=
414 l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY
);
415 notification_data
.tag
= kPushMessagingForcedNotificationTag
;
416 notification_data
.icon
= GURL(); // TODO(johnme): Better icon?
417 notification_data
.silent
= true;
418 PlatformNotificationServiceImpl
* notification_service
=
419 PlatformNotificationServiceImpl::GetInstance();
420 notification_service
->DisplayPersistentNotification(
422 service_worker_registration_id
,
424 SkBitmap() /* icon */,
429 // Other gcm::GCMAppHandler methods -------------------------------------------
431 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string
& app_id
) {
432 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
433 // corresponding to app_id.
436 void PushMessagingServiceImpl::OnSendError(
437 const std::string
& app_id
,
438 const gcm::GCMClient::SendErrorDetails
& send_error_details
) {
439 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
442 void PushMessagingServiceImpl::OnSendAcknowledged(
443 const std::string
& app_id
,
444 const std::string
& message_id
) {
445 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
448 // GetPushEndpoint method ------------------------------------------------------
450 GURL
PushMessagingServiceImpl::GetPushEndpoint() {
451 return GURL(std::string(kPushMessagingEndpoint
));
454 // Register and GetPermissionStatus methods ------------------------------------
456 void PushMessagingServiceImpl::RegisterFromDocument(
457 const GURL
& requesting_origin
,
458 int64 service_worker_registration_id
,
459 const std::string
& sender_id
,
463 const content::PushMessagingService::RegisterCallback
& callback
) {
464 PushMessagingApplicationId application_id
=
465 PushMessagingApplicationId::Generate(requesting_origin
,
466 service_worker_registration_id
);
467 DCHECK(application_id
.IsValid());
469 if (push_registration_count_
+ pending_push_registration_count_
470 >= kMaxRegistrations
) {
471 RegisterEnd(callback
,
473 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED
);
477 content::RenderFrameHost
* render_frame_host
=
478 content::RenderFrameHost::FromID(renderer_id
, render_frame_id
);
479 if (!render_frame_host
)
482 content::WebContents
* web_contents
=
483 content::WebContents::FromRenderFrameHost(render_frame_host
);
487 // TODO(miguelg) need to send this over IPC when bubble support is
491 const PermissionRequestID
id(
492 renderer_id
, web_contents
->GetRoutingID(), bridge_id
, GURL());
494 PushMessagingPermissionContext
* permission_context
=
495 PushMessagingPermissionContextFactory::GetForProfile(profile_
);
497 if (permission_context
== NULL
|| !user_visible
) {
498 RegisterEnd(callback
,
500 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
504 // TODO(miguelg): Consider the value of |user_visible| when making the
505 // permission request.
506 // TODO(mlamouri): Move requesting Push permission over to using Mojo, and
507 // re-introduce the ability of |user_gesture| when bubbles require this.
508 // https://crbug.com/423770.
509 permission_context
->RequestPermission(
510 web_contents
, id
, requesting_origin
, true /* user_gesture */,
511 base::Bind(&PushMessagingServiceImpl::DidRequestPermission
,
512 weak_factory_
.GetWeakPtr(), application_id
, sender_id
,
516 void PushMessagingServiceImpl::RegisterFromWorker(
517 const GURL
& requesting_origin
,
518 int64 service_worker_registration_id
,
519 const std::string
& sender_id
,
521 const content::PushMessagingService::RegisterCallback
& register_callback
) {
522 PushMessagingApplicationId application_id
=
523 PushMessagingApplicationId::Generate(requesting_origin
,
524 service_worker_registration_id
);
525 DCHECK(application_id
.IsValid());
527 if (profile_
->GetPrefs()->GetInteger(
528 prefs::kPushMessagingRegistrationCount
) >= kMaxRegistrations
) {
529 RegisterEnd(register_callback
, std::string(),
530 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED
);
534 // TODO(peter): Consider |user_visible| when getting the permission status
535 // for registering from a worker.
537 GURL embedding_origin
= requesting_origin
;
538 blink::WebPushPermissionStatus permission_status
=
539 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin
,
542 if (permission_status
!= blink::WebPushPermissionStatusGranted
) {
543 RegisterEnd(register_callback
, std::string(),
544 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
548 IncreasePushRegistrationCount(1, true /* is_pending */);
549 std::vector
<std::string
> sender_ids(1, sender_id
);
550 GetGCMDriver()->Register(
551 application_id
.app_id_guid(), sender_ids
,
552 base::Bind(&PushMessagingServiceImpl::DidRegister
,
553 weak_factory_
.GetWeakPtr(),
554 application_id
, register_callback
));
557 blink::WebPushPermissionStatus
PushMessagingServiceImpl::GetPermissionStatus(
558 const GURL
& requesting_origin
,
559 const GURL
& embedding_origin
,
561 // TODO(peter): Consider |user_visible| when checking Push permission.
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 gcm::GCMClient::Result result
) {
581 content::PushRegistrationStatus status
=
582 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR
;
584 case gcm::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 gcm::GCMClient::INVALID_PARAMETER
:
590 case gcm::GCMClient::GCM_DISABLED
:
591 case gcm::GCMClient::ASYNC_OPERATION_PENDING
:
592 case gcm::GCMClient::SERVER_ERROR
:
593 case gcm::GCMClient::UNKNOWN_ERROR
:
594 status
= content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR
;
596 case gcm::GCMClient::NETWORK_ERROR
:
597 case gcm::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 IncreasePushRegistrationCount(1, true /* is_pending */);
618 std::vector
<std::string
> sender_ids(1, sender_id
);
619 GetGCMDriver()->Register(
620 application_id
.app_id_guid(),
622 base::Bind(&PushMessagingServiceImpl::DidRegister
,
623 weak_factory_
.GetWeakPtr(),
624 application_id
, register_callback
));
627 // Unregister methods ----------------------------------------------------------
629 void PushMessagingServiceImpl::Unregister(
630 const GURL
& requesting_origin
,
631 int64 service_worker_registration_id
,
632 const std::string
& sender_id
,
633 const content::PushMessagingService::UnregisterCallback
& callback
) {
634 PushMessagingApplicationId application_id
= PushMessagingApplicationId::Get(
635 profile_
, requesting_origin
, service_worker_registration_id
);
636 if (!application_id
.IsValid()) {
637 if (!callback
.is_null()) {
639 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED
);
644 Unregister(application_id
.app_id_guid(), sender_id
, callback
);
647 void PushMessagingServiceImpl::Unregister(
648 const std::string
& app_id_guid
,
649 const std::string
& sender_id
,
650 const content::PushMessagingService::UnregisterCallback
& callback
) {
651 // Delete the mapping for this app id, to guarantee that no messages get
652 // delivered in future (even if unregistration fails).
653 // TODO(johnme): Instead of deleting these app ids, store them elsewhere, and
654 // retry unregistration if it fails due to network errors (crbug.com/465399).
655 PushMessagingApplicationId application_id
=
656 PushMessagingApplicationId::Get(profile_
, app_id_guid
);
657 bool was_registered
= application_id
.IsValid();
659 application_id
.DeleteFromDisk(profile_
);
661 const auto& unregister_callback
=
662 base::Bind(&PushMessagingServiceImpl::DidUnregister
,
663 weak_factory_
.GetWeakPtr(),
664 was_registered
, callback
);
665 #if defined(OS_ANDROID)
666 // On Android the backend is different, and requires the original sender_id.
667 // UnregisterBecausePermissionRevoked sometimes calls us with an empty one.
668 if (sender_id
.empty())
669 unregister_callback
.Run(gcm::GCMClient::INVALID_PARAMETER
);
671 GetGCMDriver()->UnregisterWithSenderId(app_id_guid
, sender_id
,
672 unregister_callback
);
674 GetGCMDriver()->Unregister(app_id_guid
, unregister_callback
);
678 void PushMessagingServiceImpl::DidUnregister(
680 const content::PushMessagingService::UnregisterCallback
& callback
,
681 gcm::GCMClient::Result result
) {
683 DecreasePushRegistrationCount(1, false /* was_pending */);
685 // Internal calls pass a null callback.
686 if (callback
.is_null())
689 if (!was_registered
) {
691 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED
);
695 case gcm::GCMClient::SUCCESS
:
696 callback
.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED
);
698 case gcm::GCMClient::INVALID_PARAMETER
:
699 case gcm::GCMClient::GCM_DISABLED
:
700 case gcm::GCMClient::SERVER_ERROR
:
701 case gcm::GCMClient::UNKNOWN_ERROR
:
702 callback
.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR
);
704 case gcm::GCMClient::ASYNC_OPERATION_PENDING
:
705 case gcm::GCMClient::NETWORK_ERROR
:
706 case gcm::GCMClient::TTL_EXCEEDED
:
707 callback
.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR
);
712 // OnContentSettingChanged methods ---------------------------------------------
714 void PushMessagingServiceImpl::OnContentSettingChanged(
715 const ContentSettingsPattern
& primary_pattern
,
716 const ContentSettingsPattern
& secondary_pattern
,
717 ContentSettingsType content_type
,
718 std::string resource_identifier
) {
719 if (content_type
!= CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
&&
720 content_type
!= CONTENT_SETTINGS_TYPE_NOTIFICATIONS
) {
724 std::vector
<PushMessagingApplicationId
> all_app_ids
=
725 PushMessagingApplicationId::GetAll(profile_
);
727 base::Closure barrier_closure
= base::BarrierClosure(
729 content_setting_changed_callback_for_testing_
.is_null()
730 ? base::Bind(&base::DoNothing
)
731 : content_setting_changed_callback_for_testing_
);
733 for (const auto& id
: all_app_ids
) {
734 // If |primary_pattern| is not valid, we should always check for a
735 // permission change because it can happen for example when the entire
736 // Push or Notifications permissions are cleared.
737 // Otherwise, the permission should be checked if the pattern matches the
739 if (primary_pattern
.IsValid() && !primary_pattern
.Matches(id
.origin())) {
740 barrier_closure
.Run();
744 if (HasPermission(id
.origin())) {
745 barrier_closure
.Run();
750 profile_
, id
.origin(), id
.service_worker_registration_id(),
752 &PushMessagingServiceImpl::UnregisterBecausePermissionRevoked
,
753 weak_factory_
.GetWeakPtr(), id
, barrier_closure
));
757 void PushMessagingServiceImpl::UnregisterBecausePermissionRevoked(
758 const PushMessagingApplicationId
& id
, const base::Closure
& closure
,
759 const std::string
& sender_id
, bool success
, bool not_found
) {
760 base::Closure barrier_closure
= base::BarrierClosure(2, closure
);
762 // Unregister the PushMessagingApplicationId with the push service.
763 // It's possible for GetSenderId to have failed and sender_id to be empty, if
764 // cookies (and the SW database) for an origin got cleared before permissions
765 // are cleared for the origin. In that case Unregister will just delete the
766 // application ID to block future messages.
767 // TODO(johnme): Auto-unregister before SW DB is cleared
768 // (https://crbug.com/402458).
769 Unregister(id
.app_id_guid(), sender_id
,
770 base::Bind(&UnregisterCallbackToClosure
, barrier_closure
));
772 // Clear the associated service worker push registration id.
773 ClearPushRegistrationID(profile_
, id
.origin(),
774 id
.service_worker_registration_id(), barrier_closure
);
777 void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting(
778 const base::Closure
& callback
) {
779 content_setting_changed_callback_for_testing_
= callback
;
782 // KeyedService methods -------------------------------------------------------
784 void PushMessagingServiceImpl::Shutdown() {
785 GetGCMDriver()->RemoveAppHandler(kPushMessagingApplicationIdPrefix
);
788 // Helper methods --------------------------------------------------------------
790 bool PushMessagingServiceImpl::HasPermission(const GURL
& origin
) {
791 PushMessagingPermissionContext
* permission_context
=
792 PushMessagingPermissionContextFactory::GetForProfile(profile_
);
793 DCHECK(permission_context
);
795 return permission_context
->GetPermissionStatus(origin
, origin
) ==
796 CONTENT_SETTING_ALLOW
;
799 gcm::GCMDriver
* PushMessagingServiceImpl::GetGCMDriver() const {
800 gcm::GCMProfileService
* gcm_profile_service
=
801 gcm::GCMProfileServiceFactory::GetForProfile(profile_
);
802 CHECK(gcm_profile_service
);
803 CHECK(gcm_profile_service
->driver());
804 return gcm_profile_service
->driver();