Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / push_messaging / push_messaging_service_impl.cc
blobf5b7bcd2afce888f7c29414fc18cb29f30cf25a2
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"
7 #include <vector>
9 #include "base/barrier_closure.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/prefs/pref_service.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
18 #include "chrome/browser/permissions/permission_manager.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
21 #include "chrome/browser/push_messaging/push_messaging_constants.h"
22 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
23 #include "chrome/browser/services/gcm/gcm_profile_service.h"
24 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
25 #include "chrome/browser/ui/chrome_pages.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/grit/generated_resources.h"
29 #include "components/content_settings/core/browser/host_content_settings_map.h"
30 #include "components/gcm_driver/gcm_driver.h"
31 #include "components/pref_registry/pref_registry_syncable.h"
32 #include "components/rappor/rappor_utils.h"
33 #include "content/public/browser/browser_context.h"
34 #include "content/public/browser/permission_type.h"
35 #include "content/public/browser/render_frame_host.h"
36 #include "content/public/browser/service_worker_context.h"
37 #include "content/public/browser/storage_partition.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/common/child_process_host.h"
40 #include "content/public/common/content_switches.h"
41 #include "content/public/common/push_messaging_status.h"
42 #include "ui/base/l10n/l10n_util.h"
44 #if defined(ENABLE_BACKGROUND)
45 #include "chrome/browser/background/background_mode_manager.h"
46 #endif
48 namespace {
49 const int kMaxRegistrations = 1000000;
51 // Chrome does not yet support silent push messages, and requires websites to
52 // indicate that they will only send user-visible messages.
53 const char kSilentPushUnsupportedMessage[] =
54 "Chrome currently only supports the Push API for subscriptions that will "
55 "result in user-visible messages. You can indicate this by calling "
56 "pushManager.subscribe({userVisibleOnly: true}) instead. See "
57 "https://goo.gl/yqv4Q4 for more details.";
59 void RecordDeliveryStatus(content::PushDeliveryStatus status) {
60 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
61 status,
62 content::PUSH_DELIVERY_STATUS_LAST + 1);
65 blink::WebPushPermissionStatus ToPushPermission(
66 content::PermissionStatus permission_status) {
67 switch (permission_status) {
68 case content::PERMISSION_STATUS_GRANTED:
69 return blink::WebPushPermissionStatusGranted;
70 case content::PERMISSION_STATUS_DENIED:
71 return blink::WebPushPermissionStatusDenied;
72 case content::PERMISSION_STATUS_ASK:
73 return blink::WebPushPermissionStatusPrompt;
74 default:
75 NOTREACHED();
76 return blink::WebPushPermissionStatusDenied;
80 void UnregisterCallbackToClosure(
81 const base::Closure& closure, content::PushUnregistrationStatus status) {
82 closure.Run();
85 #if defined(ENABLE_BACKGROUND)
86 bool UseBackgroundMode() {
87 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
88 if (command_line->HasSwitch(switches::kDisablePushApiBackgroundMode))
89 return false;
90 return command_line->HasSwitch(switches::kEnablePushApiBackgroundMode);
92 #endif // defined(ENABLE_BACKGROUND)
94 } // namespace
96 // static
97 void PushMessagingServiceImpl::RegisterProfilePrefs(
98 user_prefs::PrefRegistrySyncable* registry) {
99 registry->RegisterIntegerPref(prefs::kPushMessagingRegistrationCount, 0);
100 PushMessagingAppIdentifier::RegisterProfilePrefs(registry);
103 // static
104 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
105 // TODO(johnme): Consider whether push should be enabled in incognito.
106 if (!profile || profile->IsOffTheRecord())
107 return;
109 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
110 // pref), be careful that this pref is read from the right profile, as prefs
111 // defined in a regular profile are visible in the corresponding incognito
112 // profile unless overridden.
113 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
114 int count = profile->GetPrefs()->GetInteger(
115 prefs::kPushMessagingRegistrationCount);
116 if (count <= 0)
117 return;
119 PushMessagingServiceImpl* push_service =
120 PushMessagingServiceFactory::GetForProfile(profile);
121 push_service->IncreasePushSubscriptionCount(count, false /* is_pending */);
124 PushMessagingServiceImpl::PushMessagingServiceImpl(Profile* profile)
125 : profile_(profile),
126 push_subscription_count_(0),
127 pending_push_subscription_count_(0),
128 #if defined(ENABLE_NOTIFICATIONS)
129 notification_manager_(profile),
130 #endif
131 weak_factory_(this) {
132 DCHECK(profile);
133 HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(this);
136 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
139 void PushMessagingServiceImpl::IncreasePushSubscriptionCount(int add,
140 bool is_pending) {
141 DCHECK(add > 0);
142 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
143 GetGCMDriver()->AddAppHandler(kPushMessagingAppIdentifierPrefix, this);
145 if (is_pending) {
146 pending_push_subscription_count_ += add;
147 } else {
148 #if defined(ENABLE_BACKGROUND)
149 if (UseBackgroundMode() && g_browser_process->background_mode_manager() &&
150 !push_subscription_count_) {
151 g_browser_process->background_mode_manager()->RegisterTrigger(
152 profile_, this, false /* should_notify_user */);
154 #endif // defined(ENABLE_BACKGROUND)
155 push_subscription_count_ += add;
156 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
157 push_subscription_count_);
161 void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
162 bool was_pending) {
163 DCHECK(subtract > 0);
164 if (was_pending) {
165 pending_push_subscription_count_ -= subtract;
166 DCHECK(pending_push_subscription_count_ >= 0);
167 } else {
168 push_subscription_count_ -= subtract;
169 DCHECK(push_subscription_count_ >= 0);
170 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
171 push_subscription_count_);
173 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
174 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
176 #if defined(ENABLE_BACKGROUND)
177 if (UseBackgroundMode() && g_browser_process->background_mode_manager()) {
178 g_browser_process->background_mode_manager()->UnregisterTrigger(profile_,
179 this);
181 #endif // defined(ENABLE_BACKGROUND)
185 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
186 return !PushMessagingAppIdentifier::FindByAppId(profile_, app_id).is_null();
189 void PushMessagingServiceImpl::ShutdownHandler() {
190 // Shutdown() should come before and it removes us from the list of app
191 // handlers of gcm::GCMDriver so this shouldn't ever been called.
192 NOTREACHED();
195 // OnMessage methods -----------------------------------------------------------
197 void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
198 const gcm::IncomingMessage& message) {
199 in_flight_message_deliveries_.insert(app_id);
201 base::Closure message_handled_closure =
202 message_callback_for_testing_.is_null() ? base::Bind(&base::DoNothing)
203 : message_callback_for_testing_;
204 PushMessagingAppIdentifier app_identifier =
205 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
206 // Drop message and unregister if app_id was unknown (maybe recently deleted).
207 if (app_identifier.is_null()) {
208 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message,
209 message_handled_closure,
210 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID);
211 return;
213 // Drop message and unregister if |origin| has lost push permission.
214 if (!IsPermissionSet(app_identifier.origin())) {
215 DeliverMessageCallback(app_id, app_identifier.origin(),
216 app_identifier.service_worker_registration_id(),
217 message, message_handled_closure,
218 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED);
219 return;
222 rappor::SampleDomainAndRegistryFromGURL(
223 g_browser_process->rappor_service(),
224 "PushMessaging.MessageReceived.Origin",
225 app_identifier.origin());
227 // The Push API only exposes a single string of data in the push event fired
228 // on the Service Worker. When developers send messages using GCM to the Push
229 // API and want to include a message payload, they must pass a single key-
230 // value pair, where the key is "data" and the value is the string they want
231 // to be passed to their Service Worker. For example, they could send the
232 // following JSON using the HTTPS GCM API:
233 // {
234 // "registration_ids": ["FOO", "BAR"],
235 // "data": {
236 // "data": "BAZ",
237 // },
238 // "delay_while_idle": true,
239 // }
240 // TODO(johnme): Make sure this is clearly documented for developers.
241 std::string data;
242 // TODO(peter): Message payloads are disabled pending mandatory encryption.
243 // https://crbug.com/449184
244 if (AreMessagePayloadsEnabled()) {
245 gcm::MessageData::const_iterator it = message.data.find("data");
246 if (it != message.data.end())
247 data = it->second;
250 content::BrowserContext::DeliverPushMessage(
251 profile_,
252 app_identifier.origin(),
253 app_identifier.service_worker_registration_id(),
254 data,
255 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
256 weak_factory_.GetWeakPtr(),
257 app_identifier.app_id(), app_identifier.origin(),
258 app_identifier.service_worker_registration_id(), message,
259 message_handled_closure));
262 void PushMessagingServiceImpl::DeliverMessageCallback(
263 const std::string& app_id,
264 const GURL& requesting_origin,
265 int64 service_worker_registration_id,
266 const gcm::IncomingMessage& message,
267 const base::Closure& message_handled_closure,
268 content::PushDeliveryStatus status) {
269 // Remove a single in-flight delivery for |app_id|. This has to be done using
270 // an iterator rather than by value, as the latter removes all entries.
271 DCHECK(in_flight_message_deliveries_.find(app_id) !=
272 in_flight_message_deliveries_.end());
274 in_flight_message_deliveries_.erase(
275 in_flight_message_deliveries_.find(app_id));
277 // TODO(mvanouwerkerk): Show a warning in the developer console of the
278 // Service Worker corresponding to app_id (and/or on an internals page).
279 // See https://crbug.com/508516 for options.
280 switch (status) {
281 // Call EnforceUserVisibleOnlyRequirements if the message was delivered to
282 // the Service Worker JavaScript, even if the website's event handler failed
283 // (to prevent sites deliberately failing in order to avoid having to show
284 // notifications).
285 case content::PUSH_DELIVERY_STATUS_SUCCESS:
286 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
287 #if defined(ENABLE_NOTIFICATIONS)
288 // Only enforce the user visible requirements after the entire queue of
289 // incoming messages for |app_id| has been flushed.
290 if (!in_flight_message_deliveries_.count(app_id)) {
291 notification_manager_.EnforceUserVisibleOnlyRequirements(
292 requesting_origin, service_worker_registration_id,
293 message_handled_closure);
294 } else {
295 message_handled_closure.Run();
297 #else
298 message_handled_closure.Run();
299 #endif
300 break;
301 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
302 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
303 message_handled_closure.Run();
304 break;
305 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
306 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
307 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
308 Unsubscribe(
309 app_id, message.sender_id,
310 base::Bind(&UnregisterCallbackToClosure, message_handled_closure));
311 break;
314 RecordDeliveryStatus(status);
317 void PushMessagingServiceImpl::SetMessageCallbackForTesting(
318 const base::Closure& callback) {
319 message_callback_for_testing_ = callback;
322 // Other gcm::GCMAppHandler methods -------------------------------------------
324 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
325 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
326 // corresponding to app_id.
329 void PushMessagingServiceImpl::OnSendError(
330 const std::string& app_id,
331 const gcm::GCMClient::SendErrorDetails& send_error_details) {
332 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
335 void PushMessagingServiceImpl::OnSendAcknowledged(
336 const std::string& app_id,
337 const std::string& message_id) {
338 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
341 // GetPushEndpoint method ------------------------------------------------------
343 GURL PushMessagingServiceImpl::GetPushEndpoint() {
344 return GURL(std::string(kPushMessagingEndpoint));
347 // Subscribe and GetPermissionStatus methods -----------------------------------
349 void PushMessagingServiceImpl::SubscribeFromDocument(
350 const GURL& requesting_origin,
351 int64 service_worker_registration_id,
352 const std::string& sender_id,
353 int renderer_id,
354 int render_frame_id,
355 bool user_visible,
356 const content::PushMessagingService::RegisterCallback& callback) {
357 PushMessagingAppIdentifier app_identifier =
358 PushMessagingAppIdentifier::Generate(requesting_origin,
359 service_worker_registration_id);
361 if (push_subscription_count_ + pending_push_subscription_count_ >=
362 kMaxRegistrations) {
363 SubscribeEndWithError(callback,
364 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
365 return;
368 content::RenderFrameHost* render_frame_host =
369 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
370 content::WebContents* web_contents =
371 content::WebContents::FromRenderFrameHost(render_frame_host);
372 if (!web_contents)
373 return;
375 if (!user_visible) {
376 web_contents->GetMainFrame()->AddMessageToConsole(
377 content::CONSOLE_MESSAGE_LEVEL_ERROR,
378 kSilentPushUnsupportedMessage);
380 SubscribeEndWithError(callback,
381 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
382 return;
385 // Push does not allow permission requests from iframes.
386 int request_id = -1;
388 profile_->GetPermissionManager()->RequestPermission(
389 content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(),
390 request_id, requesting_origin, true /* user_gesture */,
391 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
392 weak_factory_.GetWeakPtr(), app_identifier, sender_id,
393 callback));
396 void PushMessagingServiceImpl::SubscribeFromWorker(
397 const GURL& requesting_origin,
398 int64 service_worker_registration_id,
399 const std::string& sender_id,
400 bool user_visible,
401 const content::PushMessagingService::RegisterCallback& register_callback) {
402 PushMessagingAppIdentifier app_identifier =
403 PushMessagingAppIdentifier::Generate(requesting_origin,
404 service_worker_registration_id);
406 if (profile_->GetPrefs()->GetInteger(
407 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
408 SubscribeEndWithError(register_callback,
409 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
410 return;
413 GURL embedding_origin = requesting_origin;
414 blink::WebPushPermissionStatus permission_status =
415 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
416 embedding_origin,
417 user_visible);
418 if (permission_status != blink::WebPushPermissionStatusGranted) {
419 SubscribeEndWithError(register_callback,
420 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
421 return;
424 IncreasePushSubscriptionCount(1, true /* is_pending */);
425 std::vector<std::string> sender_ids(1, sender_id);
426 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
427 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
428 weak_factory_.GetWeakPtr(),
429 app_identifier, register_callback));
432 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
433 const GURL& requesting_origin,
434 const GURL& embedding_origin,
435 bool user_visible) {
436 if (!user_visible)
437 return blink::WebPushPermissionStatusDenied;
439 return ToPushPermission(profile_->GetPermissionManager()->GetPermissionStatus(
440 content::PermissionType::PUSH_MESSAGING, requesting_origin,
441 embedding_origin));
444 bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
445 return false;
448 void PushMessagingServiceImpl::SubscribeEnd(
449 const content::PushMessagingService::RegisterCallback& callback,
450 const std::string& subscription_id,
451 const std::vector<uint8_t>& curve25519dh,
452 content::PushRegistrationStatus status) {
453 callback.Run(subscription_id, curve25519dh, status);
456 void PushMessagingServiceImpl::SubscribeEndWithError(
457 const content::PushMessagingService::RegisterCallback& callback,
458 content::PushRegistrationStatus status) {
459 SubscribeEnd(callback, std::string() /* subscription_id */,
460 std::vector<uint8_t>() /* curve25519dh */, status);
463 void PushMessagingServiceImpl::DidSubscribe(
464 const PushMessagingAppIdentifier& app_identifier,
465 const content::PushMessagingService::RegisterCallback& callback,
466 const std::string& subscription_id,
467 gcm::GCMClient::Result result) {
468 DecreasePushSubscriptionCount(1, true /* was_pending */);
470 content::PushRegistrationStatus status =
471 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
473 switch (result) {
474 case gcm::GCMClient::SUCCESS:
475 // Do not get a certificate if message payloads have not been enabled.
476 if (!AreMessagePayloadsEnabled()) {
477 DidSubscribeWithPublicKey(
478 app_identifier, callback, subscription_id,
479 std::string() /* public_key */);
480 return;
483 // Make sure that this subscription has associated encryption keys prior
484 // to returning it to the developer - they'll need this information in
485 // order to send payloads to the user.
486 GetGCMDriver()->GetPublicKey(
487 app_identifier.app_id(),
488 base::Bind(
489 &PushMessagingServiceImpl::DidSubscribeWithPublicKey,
490 weak_factory_.GetWeakPtr(), app_identifier, callback,
491 subscription_id));
493 return;
494 case gcm::GCMClient::INVALID_PARAMETER:
495 case gcm::GCMClient::GCM_DISABLED:
496 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
497 case gcm::GCMClient::SERVER_ERROR:
498 case gcm::GCMClient::UNKNOWN_ERROR:
499 status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
500 break;
501 case gcm::GCMClient::NETWORK_ERROR:
502 case gcm::GCMClient::TTL_EXCEEDED:
503 status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
504 break;
507 SubscribeEndWithError(callback, status);
510 void PushMessagingServiceImpl::DidSubscribeWithPublicKey(
511 const PushMessagingAppIdentifier& app_identifier,
512 const content::PushMessagingService::RegisterCallback& callback,
513 const std::string& subscription_id,
514 const std::string& public_key) {
515 if (!public_key.size() && AreMessagePayloadsEnabled()) {
516 SubscribeEndWithError(
517 callback, content::PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE);
518 return;
521 app_identifier.PersistToPrefs(profile_);
523 IncreasePushSubscriptionCount(1, false /* is_pending */);
525 SubscribeEnd(callback, subscription_id,
526 std::vector<uint8_t>(public_key.begin(), public_key.end()),
527 content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE);
530 void PushMessagingServiceImpl::DidRequestPermission(
531 const PushMessagingAppIdentifier& app_identifier,
532 const std::string& sender_id,
533 const content::PushMessagingService::RegisterCallback& register_callback,
534 content::PermissionStatus permission_status) {
535 if (permission_status != content::PERMISSION_STATUS_GRANTED) {
536 SubscribeEndWithError(register_callback,
537 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
538 return;
541 IncreasePushSubscriptionCount(1, true /* is_pending */);
542 std::vector<std::string> sender_ids(1, sender_id);
543 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
544 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
545 weak_factory_.GetWeakPtr(),
546 app_identifier, register_callback));
549 // GetPublicEncryptionKey methods ----------------------------------------------
551 void PushMessagingServiceImpl::GetPublicEncryptionKey(
552 const GURL& origin,
553 int64_t service_worker_registration_id,
554 const PushMessagingService::PublicKeyCallback& callback) {
555 // An empty public key will be returned if payloads are not enabled.
556 if (!AreMessagePayloadsEnabled()) {
557 callback.Run(true /* success */, std::vector<uint8_t>());
558 return;
561 PushMessagingAppIdentifier app_identifier =
562 PushMessagingAppIdentifier::FindByServiceWorker(
563 profile_, origin, service_worker_registration_id);
565 DCHECK(!app_identifier.is_null());
567 GetGCMDriver()->GetPublicKey(
568 app_identifier.app_id(),
569 base::Bind(&PushMessagingServiceImpl::DidGetPublicKey,
570 weak_factory_.GetWeakPtr(), callback));
573 void PushMessagingServiceImpl::DidGetPublicKey(
574 const PushMessagingService::PublicKeyCallback& callback,
575 const std::string& public_key) const {
576 // I/O errors might prevent the GCM Driver from retrieving a key-pair.
577 const bool success = !!public_key.size();
579 callback.Run(success, std::vector<uint8_t>(public_key.begin(),
580 public_key.end()));
583 // Unsubscribe methods ---------------------------------------------------------
585 void PushMessagingServiceImpl::Unsubscribe(
586 const GURL& requesting_origin,
587 int64 service_worker_registration_id,
588 const std::string& sender_id,
589 const content::PushMessagingService::UnregisterCallback& callback) {
590 PushMessagingAppIdentifier app_identifier =
591 PushMessagingAppIdentifier::FindByServiceWorker(
592 profile_, requesting_origin, service_worker_registration_id);
593 if (app_identifier.is_null()) {
594 if (!callback.is_null()) {
595 callback.Run(
596 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
598 return;
601 Unsubscribe(app_identifier.app_id(), sender_id, callback);
604 void PushMessagingServiceImpl::Unsubscribe(
605 const std::string& app_id,
606 const std::string& sender_id,
607 const content::PushMessagingService::UnregisterCallback& callback) {
608 // Delete the mapping for this app_id, to guarantee that no messages get
609 // delivered in future (even if unregistration fails).
610 // TODO(johnme): Instead of deleting these app ids, store them elsewhere, and
611 // retry unregistration if it fails due to network errors (crbug.com/465399).
612 PushMessagingAppIdentifier app_identifier =
613 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
614 bool was_registered = !app_identifier.is_null();
615 if (was_registered)
616 app_identifier.DeleteFromPrefs(profile_);
618 const auto& unregister_callback =
619 base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
620 weak_factory_.GetWeakPtr(), was_registered, callback);
621 #if defined(OS_ANDROID)
622 // On Android the backend is different, and requires the original sender_id.
623 // UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
624 if (sender_id.empty())
625 unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
626 else
627 GetGCMDriver()->UnregisterWithSenderId(app_id, sender_id,
628 unregister_callback);
629 #else
630 GetGCMDriver()->Unregister(app_id, unregister_callback);
631 #endif
634 void PushMessagingServiceImpl::DidUnsubscribe(
635 bool was_subscribed,
636 const content::PushMessagingService::UnregisterCallback& callback,
637 gcm::GCMClient::Result result) {
638 if (was_subscribed)
639 DecreasePushSubscriptionCount(1, false /* was_pending */);
641 // Internal calls pass a null callback.
642 if (callback.is_null())
643 return;
645 if (!was_subscribed) {
646 callback.Run(
647 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
648 return;
650 switch (result) {
651 case gcm::GCMClient::SUCCESS:
652 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
653 break;
654 case gcm::GCMClient::INVALID_PARAMETER:
655 case gcm::GCMClient::GCM_DISABLED:
656 case gcm::GCMClient::SERVER_ERROR:
657 case gcm::GCMClient::UNKNOWN_ERROR:
658 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR);
659 break;
660 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
661 case gcm::GCMClient::NETWORK_ERROR:
662 case gcm::GCMClient::TTL_EXCEEDED:
663 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR);
664 break;
668 // OnContentSettingChanged methods ---------------------------------------------
670 void PushMessagingServiceImpl::OnContentSettingChanged(
671 const ContentSettingsPattern& primary_pattern,
672 const ContentSettingsPattern& secondary_pattern,
673 ContentSettingsType content_type,
674 std::string resource_identifier) {
675 if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
676 content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
677 return;
680 std::vector<PushMessagingAppIdentifier> all_app_identifiers =
681 PushMessagingAppIdentifier::GetAll(profile_);
683 base::Closure barrier_closure = base::BarrierClosure(
684 all_app_identifiers.size(),
685 content_setting_changed_callback_for_testing_.is_null()
686 ? base::Bind(&base::DoNothing)
687 : content_setting_changed_callback_for_testing_);
689 for (const PushMessagingAppIdentifier& app_identifier : all_app_identifiers) {
690 // If |primary_pattern| is not valid, we should always check for a
691 // permission change because it can happen for example when the entire
692 // Push or Notifications permissions are cleared.
693 // Otherwise, the permission should be checked if the pattern matches the
694 // origin.
695 if (primary_pattern.IsValid() &&
696 !primary_pattern.Matches(app_identifier.origin())) {
697 barrier_closure.Run();
698 continue;
701 if (IsPermissionSet(app_identifier.origin())) {
702 barrier_closure.Run();
703 continue;
706 GetSenderId(
707 profile_, app_identifier.origin(),
708 app_identifier.service_worker_registration_id(),
709 base::Bind(
710 &PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
711 weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
715 void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked(
716 const PushMessagingAppIdentifier& app_identifier,
717 const base::Closure& closure,
718 const std::string& sender_id,
719 bool success,
720 bool not_found) {
721 base::Closure barrier_closure = base::BarrierClosure(2, closure);
723 // Unsubscribe the PushMessagingAppIdentifier with the push service.
724 // It's possible for GetSenderId to have failed and sender_id to be empty, if
725 // cookies (and the SW database) for an origin got cleared before permissions
726 // are cleared for the origin. In that case Unsubscribe will just delete the
727 // app identifier to block future messages.
728 // TODO(johnme): Auto-unregister before SW DB is cleared
729 // (https://crbug.com/402458).
730 Unsubscribe(app_identifier.app_id(), sender_id,
731 base::Bind(&UnregisterCallbackToClosure, barrier_closure));
733 // Clear the associated service worker push registration id.
734 ClearPushSubscriptionID(profile_, app_identifier.origin(),
735 app_identifier.service_worker_registration_id(),
736 barrier_closure);
739 void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting(
740 const base::Closure& callback) {
741 content_setting_changed_callback_for_testing_ = callback;
744 // KeyedService methods -------------------------------------------------------
746 void PushMessagingServiceImpl::Shutdown() {
747 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
748 profile_->GetHostContentSettingsMap()->RemoveObserver(this);
749 #if defined(ENABLE_BACKGROUND)
750 // TODO(mvanouwerkerk): Ensure Push API unregisters correctly from Background
751 // Mode - crbug.com/527036.
752 if (UseBackgroundMode() && g_browser_process->background_mode_manager()) {
753 g_browser_process->background_mode_manager()->UnregisterTrigger(profile_,
754 this);
756 #endif // defined(ENABLE_BACKGROUND)
759 // BackgroundTrigger methods ---------------------------------------------------
760 base::string16 PushMessagingServiceImpl::GetName() {
761 return l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME);
764 gfx::ImageSkia* PushMessagingServiceImpl::GetIcon() {
765 return nullptr;
768 void PushMessagingServiceImpl::OnMenuClick() {
769 #if defined(ENABLE_BACKGROUND)
770 chrome::ShowContentSettings(
771 BackgroundModeManager::GetBrowserWindowForProfile(profile_),
772 CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
773 #endif // defined(ENABLE_BACKGROUND)
776 // Helper methods --------------------------------------------------------------
778 // Assumes user_visible always since this is just meant to check
779 // if the permission was previously granted and not revoked.
780 bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
781 return GetPermissionStatus(origin, origin, true /* user_visible */) ==
782 blink::WebPushPermissionStatusGranted;
785 bool PushMessagingServiceImpl::AreMessagePayloadsEnabled() const {
786 return base::CommandLine::ForCurrentProcess()->HasSwitch(
787 switches::kEnablePushMessagePayload);
790 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
791 gcm::GCMProfileService* gcm_profile_service =
792 gcm::GCMProfileServiceFactory::GetForProfile(profile_);
793 CHECK(gcm_profile_service);
794 CHECK(gcm_profile_service->driver());
795 return gcm_profile_service->driver();