Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / push_messaging / push_messaging_service_impl.cc
blobd8a0ef3498b131e6142a80b737c9dc93726f6623
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/field_trial.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
19 #include "chrome/browser/permissions/permission_manager.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
22 #include "chrome/browser/push_messaging/push_messaging_constants.h"
23 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
24 #include "chrome/browser/services/gcm/gcm_profile_service.h"
25 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
26 #include "chrome/browser/ui/chrome_pages.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/gcm_driver/gcm_driver.h"
32 #include "components/pref_registry/pref_registry_syncable.h"
33 #include "components/rappor/rappor_utils.h"
34 #include "content/public/browser/browser_context.h"
35 #include "content/public/browser/permission_type.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/push_messaging_status.h"
43 #include "ui/base/l10n/l10n_util.h"
45 #if defined(ENABLE_BACKGROUND)
46 #include "chrome/browser/background/background_mode_manager.h"
47 #endif
49 namespace {
50 const int kMaxRegistrations = 1000000;
52 // Chrome does not yet support silent push messages, and requires websites to
53 // indicate that they will only send user-visible messages.
54 const char kSilentPushUnsupportedMessage[] =
55 "Chrome currently only supports the Push API for subscriptions that will "
56 "result in user-visible messages. You can indicate this by calling "
57 "pushManager.subscribe({userVisibleOnly: true}) instead. See "
58 "https://goo.gl/yqv4Q4 for more details.";
60 void RecordDeliveryStatus(content::PushDeliveryStatus status) {
61 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
62 status,
63 content::PUSH_DELIVERY_STATUS_LAST + 1);
66 blink::WebPushPermissionStatus ToPushPermission(
67 content::PermissionStatus permission_status) {
68 switch (permission_status) {
69 case content::PERMISSION_STATUS_GRANTED:
70 return blink::WebPushPermissionStatusGranted;
71 case content::PERMISSION_STATUS_DENIED:
72 return blink::WebPushPermissionStatusDenied;
73 case content::PERMISSION_STATUS_ASK:
74 return blink::WebPushPermissionStatusPrompt;
75 default:
76 NOTREACHED();
77 return blink::WebPushPermissionStatusDenied;
81 void UnregisterCallbackToClosure(
82 const base::Closure& closure, content::PushUnregistrationStatus status) {
83 closure.Run();
86 #if defined(ENABLE_BACKGROUND)
87 bool UseBackgroundMode() {
88 // Note: if push is ever enabled in incognito, the background mode integration
89 // should not be enabled for it.
90 std::string group_name =
91 base::FieldTrialList::FindFullName("PushApiBackgroundMode");
92 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
93 if (command_line->HasSwitch(switches::kDisablePushApiBackgroundMode))
94 return false;
95 if (command_line->HasSwitch(switches::kEnablePushApiBackgroundMode))
96 return true;
97 return group_name == "Enabled";
99 #endif // defined(ENABLE_BACKGROUND)
101 } // namespace
103 // static
104 void PushMessagingServiceImpl::RegisterProfilePrefs(
105 user_prefs::PrefRegistrySyncable* registry) {
106 registry->RegisterIntegerPref(prefs::kPushMessagingRegistrationCount, 0);
107 PushMessagingAppIdentifier::RegisterProfilePrefs(registry);
110 // static
111 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
112 // TODO(johnme): Consider whether push should be enabled in incognito.
113 if (!profile || profile->IsOffTheRecord())
114 return;
116 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
117 // pref), be careful that this pref is read from the right profile, as prefs
118 // defined in a regular profile are visible in the corresponding incognito
119 // profile unless overridden.
120 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
121 int count = profile->GetPrefs()->GetInteger(
122 prefs::kPushMessagingRegistrationCount);
123 if (count <= 0)
124 return;
126 PushMessagingServiceImpl* push_service =
127 PushMessagingServiceFactory::GetForProfile(profile);
128 push_service->IncreasePushSubscriptionCount(count, false /* is_pending */);
131 PushMessagingServiceImpl::PushMessagingServiceImpl(Profile* profile)
132 : profile_(profile),
133 push_subscription_count_(0),
134 pending_push_subscription_count_(0),
135 #if defined(ENABLE_NOTIFICATIONS)
136 notification_manager_(profile),
137 #endif
138 weak_factory_(this) {
139 DCHECK(profile);
140 HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(this);
143 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
146 void PushMessagingServiceImpl::IncreasePushSubscriptionCount(int add,
147 bool is_pending) {
148 DCHECK(add > 0);
149 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
150 GetGCMDriver()->AddAppHandler(kPushMessagingAppIdentifierPrefix, this);
152 if (is_pending) {
153 pending_push_subscription_count_ += add;
154 } else {
155 #if defined(ENABLE_BACKGROUND)
156 if (UseBackgroundMode() && g_browser_process->background_mode_manager() &&
157 !push_subscription_count_) {
158 g_browser_process->background_mode_manager()->RegisterTrigger(
159 profile_, this, false /* should_notify_user */);
161 #endif // defined(ENABLE_BACKGROUND)
162 push_subscription_count_ += add;
163 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
164 push_subscription_count_);
168 void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
169 bool was_pending) {
170 DCHECK(subtract > 0);
171 if (was_pending) {
172 pending_push_subscription_count_ -= subtract;
173 DCHECK(pending_push_subscription_count_ >= 0);
174 } else {
175 push_subscription_count_ -= subtract;
176 DCHECK(push_subscription_count_ >= 0);
177 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
178 push_subscription_count_);
180 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
181 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
183 #if defined(ENABLE_BACKGROUND)
184 if (UseBackgroundMode() && g_browser_process->background_mode_manager()) {
185 g_browser_process->background_mode_manager()->UnregisterTrigger(profile_,
186 this);
188 #endif // defined(ENABLE_BACKGROUND)
192 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
193 return !PushMessagingAppIdentifier::FindByAppId(profile_, app_id).is_null();
196 void PushMessagingServiceImpl::ShutdownHandler() {
197 // Shutdown() should come before and it removes us from the list of app
198 // handlers of gcm::GCMDriver so this shouldn't ever been called.
199 NOTREACHED();
202 // OnMessage methods -----------------------------------------------------------
204 void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
205 const gcm::IncomingMessage& message) {
206 in_flight_message_deliveries_.insert(app_id);
208 base::Closure message_handled_closure =
209 message_callback_for_testing_.is_null() ? base::Bind(&base::DoNothing)
210 : message_callback_for_testing_;
211 PushMessagingAppIdentifier app_identifier =
212 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
213 // Drop message and unregister if app_id was unknown (maybe recently deleted).
214 if (app_identifier.is_null()) {
215 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message,
216 message_handled_closure,
217 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID);
218 return;
220 // Drop message and unregister if |origin| has lost push permission.
221 if (!IsPermissionSet(app_identifier.origin())) {
222 DeliverMessageCallback(app_id, app_identifier.origin(),
223 app_identifier.service_worker_registration_id(),
224 message, message_handled_closure,
225 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED);
226 return;
229 rappor::SampleDomainAndRegistryFromGURL(
230 g_browser_process->rappor_service(),
231 "PushMessaging.MessageReceived.Origin",
232 app_identifier.origin());
234 // The Push API only exposes a single string of data in the push event fired
235 // on the Service Worker. When developers send messages using GCM to the Push
236 // API and want to include a message payload, they must pass a single key-
237 // value pair, where the key is "data" and the value is the string they want
238 // to be passed to their Service Worker. For example, they could send the
239 // following JSON using the HTTPS GCM API:
240 // {
241 // "registration_ids": ["FOO", "BAR"],
242 // "data": {
243 // "data": "BAZ",
244 // },
245 // "delay_while_idle": true,
246 // }
247 // TODO(johnme): Make sure this is clearly documented for developers.
248 std::string data;
249 // TODO(peter): Message payloads are disabled pending mandatory encryption.
250 // https://crbug.com/449184
251 if (AreMessagePayloadsEnabled()) {
252 gcm::MessageData::const_iterator it = message.data.find("data");
253 if (it != message.data.end())
254 data = it->second;
257 content::BrowserContext::DeliverPushMessage(
258 profile_,
259 app_identifier.origin(),
260 app_identifier.service_worker_registration_id(),
261 data,
262 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
263 weak_factory_.GetWeakPtr(),
264 app_identifier.app_id(), app_identifier.origin(),
265 app_identifier.service_worker_registration_id(), message,
266 message_handled_closure));
269 void PushMessagingServiceImpl::DeliverMessageCallback(
270 const std::string& app_id,
271 const GURL& requesting_origin,
272 int64 service_worker_registration_id,
273 const gcm::IncomingMessage& message,
274 const base::Closure& message_handled_closure,
275 content::PushDeliveryStatus status) {
276 // Remove a single in-flight delivery for |app_id|. This has to be done using
277 // an iterator rather than by value, as the latter removes all entries.
278 DCHECK(in_flight_message_deliveries_.find(app_id) !=
279 in_flight_message_deliveries_.end());
281 in_flight_message_deliveries_.erase(
282 in_flight_message_deliveries_.find(app_id));
284 // TODO(mvanouwerkerk): Show a warning in the developer console of the
285 // Service Worker corresponding to app_id (and/or on an internals page).
286 // See https://crbug.com/508516 for options.
287 switch (status) {
288 // Call EnforceUserVisibleOnlyRequirements if the message was delivered to
289 // the Service Worker JavaScript, even if the website's event handler failed
290 // (to prevent sites deliberately failing in order to avoid having to show
291 // notifications).
292 case content::PUSH_DELIVERY_STATUS_SUCCESS:
293 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
294 #if defined(ENABLE_NOTIFICATIONS)
295 // Only enforce the user visible requirements after the entire queue of
296 // incoming messages for |app_id| has been flushed.
297 if (!in_flight_message_deliveries_.count(app_id)) {
298 notification_manager_.EnforceUserVisibleOnlyRequirements(
299 requesting_origin, service_worker_registration_id,
300 message_handled_closure);
301 } else {
302 message_handled_closure.Run();
304 #else
305 message_handled_closure.Run();
306 #endif
307 break;
308 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
309 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
310 message_handled_closure.Run();
311 break;
312 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
313 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
314 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
315 Unsubscribe(
316 app_id, message.sender_id,
317 base::Bind(&UnregisterCallbackToClosure, message_handled_closure));
318 break;
321 RecordDeliveryStatus(status);
324 void PushMessagingServiceImpl::SetMessageCallbackForTesting(
325 const base::Closure& callback) {
326 message_callback_for_testing_ = callback;
329 // Other gcm::GCMAppHandler methods -------------------------------------------
331 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
332 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
333 // corresponding to app_id.
336 void PushMessagingServiceImpl::OnSendError(
337 const std::string& app_id,
338 const gcm::GCMClient::SendErrorDetails& send_error_details) {
339 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
342 void PushMessagingServiceImpl::OnSendAcknowledged(
343 const std::string& app_id,
344 const std::string& message_id) {
345 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
348 // GetPushEndpoint method ------------------------------------------------------
350 GURL PushMessagingServiceImpl::GetPushEndpoint() {
351 return GURL(std::string(kPushMessagingEndpoint));
354 // Subscribe and GetPermissionStatus methods -----------------------------------
356 void PushMessagingServiceImpl::SubscribeFromDocument(
357 const GURL& requesting_origin,
358 int64 service_worker_registration_id,
359 const std::string& sender_id,
360 int renderer_id,
361 int render_frame_id,
362 bool user_visible,
363 const content::PushMessagingService::RegisterCallback& callback) {
364 PushMessagingAppIdentifier app_identifier =
365 PushMessagingAppIdentifier::Generate(requesting_origin,
366 service_worker_registration_id);
368 if (push_subscription_count_ + pending_push_subscription_count_ >=
369 kMaxRegistrations) {
370 SubscribeEndWithError(callback,
371 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
372 return;
375 content::RenderFrameHost* render_frame_host =
376 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
377 content::WebContents* web_contents =
378 content::WebContents::FromRenderFrameHost(render_frame_host);
379 if (!web_contents)
380 return;
382 if (!user_visible) {
383 web_contents->GetMainFrame()->AddMessageToConsole(
384 content::CONSOLE_MESSAGE_LEVEL_ERROR,
385 kSilentPushUnsupportedMessage);
387 SubscribeEndWithError(callback,
388 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
389 return;
392 // Push does not allow permission requests from iframes.
393 int request_id = -1;
395 profile_->GetPermissionManager()->RequestPermission(
396 content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(),
397 request_id, requesting_origin, true /* user_gesture */,
398 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
399 weak_factory_.GetWeakPtr(), app_identifier, sender_id,
400 callback));
403 void PushMessagingServiceImpl::SubscribeFromWorker(
404 const GURL& requesting_origin,
405 int64 service_worker_registration_id,
406 const std::string& sender_id,
407 bool user_visible,
408 const content::PushMessagingService::RegisterCallback& register_callback) {
409 PushMessagingAppIdentifier app_identifier =
410 PushMessagingAppIdentifier::Generate(requesting_origin,
411 service_worker_registration_id);
413 if (profile_->GetPrefs()->GetInteger(
414 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
415 SubscribeEndWithError(register_callback,
416 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
417 return;
420 GURL embedding_origin = requesting_origin;
421 blink::WebPushPermissionStatus permission_status =
422 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
423 embedding_origin,
424 user_visible);
425 if (permission_status != blink::WebPushPermissionStatusGranted) {
426 SubscribeEndWithError(register_callback,
427 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
428 return;
431 IncreasePushSubscriptionCount(1, true /* is_pending */);
432 std::vector<std::string> sender_ids(1, sender_id);
433 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
434 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
435 weak_factory_.GetWeakPtr(),
436 app_identifier, register_callback));
439 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
440 const GURL& requesting_origin,
441 const GURL& embedding_origin,
442 bool user_visible) {
443 if (!user_visible)
444 return blink::WebPushPermissionStatusDenied;
446 return ToPushPermission(profile_->GetPermissionManager()->GetPermissionStatus(
447 content::PermissionType::PUSH_MESSAGING, requesting_origin,
448 embedding_origin));
451 bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
452 return false;
455 void PushMessagingServiceImpl::SubscribeEnd(
456 const content::PushMessagingService::RegisterCallback& callback,
457 const std::string& subscription_id,
458 const std::vector<uint8_t>& curve25519dh,
459 content::PushRegistrationStatus status) {
460 callback.Run(subscription_id, curve25519dh, status);
463 void PushMessagingServiceImpl::SubscribeEndWithError(
464 const content::PushMessagingService::RegisterCallback& callback,
465 content::PushRegistrationStatus status) {
466 SubscribeEnd(callback, std::string() /* subscription_id */,
467 std::vector<uint8_t>() /* curve25519dh */, status);
470 void PushMessagingServiceImpl::DidSubscribe(
471 const PushMessagingAppIdentifier& app_identifier,
472 const content::PushMessagingService::RegisterCallback& callback,
473 const std::string& subscription_id,
474 gcm::GCMClient::Result result) {
475 DecreasePushSubscriptionCount(1, true /* was_pending */);
477 content::PushRegistrationStatus status =
478 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
480 switch (result) {
481 case gcm::GCMClient::SUCCESS:
482 // Do not get a certificate if message payloads have not been enabled.
483 if (!AreMessagePayloadsEnabled()) {
484 DidSubscribeWithPublicKey(
485 app_identifier, callback, subscription_id,
486 std::string() /* public_key */);
487 return;
490 // Make sure that this subscription has associated encryption keys prior
491 // to returning it to the developer - they'll need this information in
492 // order to send payloads to the user.
493 GetGCMDriver()->GetPublicKey(
494 app_identifier.app_id(),
495 base::Bind(
496 &PushMessagingServiceImpl::DidSubscribeWithPublicKey,
497 weak_factory_.GetWeakPtr(), app_identifier, callback,
498 subscription_id));
500 return;
501 case gcm::GCMClient::INVALID_PARAMETER:
502 case gcm::GCMClient::GCM_DISABLED:
503 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
504 case gcm::GCMClient::SERVER_ERROR:
505 case gcm::GCMClient::UNKNOWN_ERROR:
506 status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
507 break;
508 case gcm::GCMClient::NETWORK_ERROR:
509 case gcm::GCMClient::TTL_EXCEEDED:
510 status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
511 break;
514 SubscribeEndWithError(callback, status);
517 void PushMessagingServiceImpl::DidSubscribeWithPublicKey(
518 const PushMessagingAppIdentifier& app_identifier,
519 const content::PushMessagingService::RegisterCallback& callback,
520 const std::string& subscription_id,
521 const std::string& public_key) {
522 if (!public_key.size() && AreMessagePayloadsEnabled()) {
523 SubscribeEndWithError(
524 callback, content::PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE);
525 return;
528 app_identifier.PersistToPrefs(profile_);
530 IncreasePushSubscriptionCount(1, false /* is_pending */);
532 SubscribeEnd(callback, subscription_id,
533 std::vector<uint8_t>(public_key.begin(), public_key.end()),
534 content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE);
537 void PushMessagingServiceImpl::DidRequestPermission(
538 const PushMessagingAppIdentifier& app_identifier,
539 const std::string& sender_id,
540 const content::PushMessagingService::RegisterCallback& register_callback,
541 content::PermissionStatus permission_status) {
542 if (permission_status != content::PERMISSION_STATUS_GRANTED) {
543 SubscribeEndWithError(register_callback,
544 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
545 return;
548 IncreasePushSubscriptionCount(1, true /* is_pending */);
549 std::vector<std::string> sender_ids(1, sender_id);
550 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
551 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
552 weak_factory_.GetWeakPtr(),
553 app_identifier, register_callback));
556 // GetPublicEncryptionKey methods ----------------------------------------------
558 void PushMessagingServiceImpl::GetPublicEncryptionKey(
559 const GURL& origin,
560 int64_t service_worker_registration_id,
561 const PushMessagingService::PublicKeyCallback& callback) {
562 // An empty public key will be returned if payloads are not enabled.
563 if (!AreMessagePayloadsEnabled()) {
564 callback.Run(true /* success */, std::vector<uint8_t>());
565 return;
568 PushMessagingAppIdentifier app_identifier =
569 PushMessagingAppIdentifier::FindByServiceWorker(
570 profile_, origin, service_worker_registration_id);
572 DCHECK(!app_identifier.is_null());
574 GetGCMDriver()->GetPublicKey(
575 app_identifier.app_id(),
576 base::Bind(&PushMessagingServiceImpl::DidGetPublicKey,
577 weak_factory_.GetWeakPtr(), callback));
580 void PushMessagingServiceImpl::DidGetPublicKey(
581 const PushMessagingService::PublicKeyCallback& callback,
582 const std::string& public_key) const {
583 // I/O errors might prevent the GCM Driver from retrieving a key-pair.
584 const bool success = !!public_key.size();
586 callback.Run(success, std::vector<uint8_t>(public_key.begin(),
587 public_key.end()));
590 // Unsubscribe methods ---------------------------------------------------------
592 void PushMessagingServiceImpl::Unsubscribe(
593 const GURL& requesting_origin,
594 int64 service_worker_registration_id,
595 const std::string& sender_id,
596 const content::PushMessagingService::UnregisterCallback& callback) {
597 PushMessagingAppIdentifier app_identifier =
598 PushMessagingAppIdentifier::FindByServiceWorker(
599 profile_, requesting_origin, service_worker_registration_id);
600 if (app_identifier.is_null()) {
601 if (!callback.is_null()) {
602 callback.Run(
603 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
605 return;
608 Unsubscribe(app_identifier.app_id(), sender_id, callback);
611 void PushMessagingServiceImpl::Unsubscribe(
612 const std::string& app_id,
613 const std::string& sender_id,
614 const content::PushMessagingService::UnregisterCallback& callback) {
615 // Delete the mapping for this app_id, to guarantee that no messages get
616 // delivered in future (even if unregistration fails).
617 // TODO(johnme): Instead of deleting these app ids, store them elsewhere, and
618 // retry unregistration if it fails due to network errors (crbug.com/465399).
619 PushMessagingAppIdentifier app_identifier =
620 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
621 bool was_registered = !app_identifier.is_null();
622 if (was_registered)
623 app_identifier.DeleteFromPrefs(profile_);
625 const auto& unregister_callback =
626 base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
627 weak_factory_.GetWeakPtr(), was_registered, callback);
628 #if defined(OS_ANDROID)
629 // On Android the backend is different, and requires the original sender_id.
630 // UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
631 if (sender_id.empty())
632 unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
633 else
634 GetGCMDriver()->UnregisterWithSenderId(app_id, sender_id,
635 unregister_callback);
636 #else
637 GetGCMDriver()->Unregister(app_id, unregister_callback);
638 #endif
641 void PushMessagingServiceImpl::DidUnsubscribe(
642 bool was_subscribed,
643 const content::PushMessagingService::UnregisterCallback& callback,
644 gcm::GCMClient::Result result) {
645 if (was_subscribed)
646 DecreasePushSubscriptionCount(1, false /* was_pending */);
648 // Internal calls pass a null callback.
649 if (callback.is_null())
650 return;
652 if (!was_subscribed) {
653 callback.Run(
654 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
655 return;
657 switch (result) {
658 case gcm::GCMClient::SUCCESS:
659 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
660 break;
661 case gcm::GCMClient::INVALID_PARAMETER:
662 case gcm::GCMClient::GCM_DISABLED:
663 case gcm::GCMClient::SERVER_ERROR:
664 case gcm::GCMClient::UNKNOWN_ERROR:
665 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR);
666 break;
667 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
668 case gcm::GCMClient::NETWORK_ERROR:
669 case gcm::GCMClient::TTL_EXCEEDED:
670 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR);
671 break;
675 // OnContentSettingChanged methods ---------------------------------------------
677 void PushMessagingServiceImpl::OnContentSettingChanged(
678 const ContentSettingsPattern& primary_pattern,
679 const ContentSettingsPattern& secondary_pattern,
680 ContentSettingsType content_type,
681 std::string resource_identifier) {
682 if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
683 content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
684 return;
687 std::vector<PushMessagingAppIdentifier> all_app_identifiers =
688 PushMessagingAppIdentifier::GetAll(profile_);
690 base::Closure barrier_closure = base::BarrierClosure(
691 all_app_identifiers.size(),
692 content_setting_changed_callback_for_testing_.is_null()
693 ? base::Bind(&base::DoNothing)
694 : content_setting_changed_callback_for_testing_);
696 for (const PushMessagingAppIdentifier& app_identifier : all_app_identifiers) {
697 // If |primary_pattern| is not valid, we should always check for a
698 // permission change because it can happen for example when the entire
699 // Push or Notifications permissions are cleared.
700 // Otherwise, the permission should be checked if the pattern matches the
701 // origin.
702 if (primary_pattern.IsValid() &&
703 !primary_pattern.Matches(app_identifier.origin())) {
704 barrier_closure.Run();
705 continue;
708 if (IsPermissionSet(app_identifier.origin())) {
709 barrier_closure.Run();
710 continue;
713 GetSenderId(
714 profile_, app_identifier.origin(),
715 app_identifier.service_worker_registration_id(),
716 base::Bind(
717 &PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
718 weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
722 void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked(
723 const PushMessagingAppIdentifier& app_identifier,
724 const base::Closure& closure,
725 const std::string& sender_id,
726 bool success,
727 bool not_found) {
728 base::Closure barrier_closure = base::BarrierClosure(2, closure);
730 // Unsubscribe the PushMessagingAppIdentifier with the push service.
731 // It's possible for GetSenderId to have failed and sender_id to be empty, if
732 // cookies (and the SW database) for an origin got cleared before permissions
733 // are cleared for the origin. In that case Unsubscribe will just delete the
734 // app identifier to block future messages.
735 // TODO(johnme): Auto-unregister before SW DB is cleared
736 // (https://crbug.com/402458).
737 Unsubscribe(app_identifier.app_id(), sender_id,
738 base::Bind(&UnregisterCallbackToClosure, barrier_closure));
740 // Clear the associated service worker push registration id.
741 ClearPushSubscriptionID(profile_, app_identifier.origin(),
742 app_identifier.service_worker_registration_id(),
743 barrier_closure);
746 void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting(
747 const base::Closure& callback) {
748 content_setting_changed_callback_for_testing_ = callback;
751 // KeyedService methods -------------------------------------------------------
753 void PushMessagingServiceImpl::Shutdown() {
754 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
757 // BackgroundTrigger methods ---------------------------------------------------
758 base::string16 PushMessagingServiceImpl::GetName() {
759 return l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME);
762 gfx::ImageSkia* PushMessagingServiceImpl::GetIcon() {
763 return nullptr;
766 void PushMessagingServiceImpl::OnMenuClick() {
767 #if defined(ENABLE_BACKGROUND)
768 chrome::ShowContentSettings(
769 BackgroundModeManager::GetBrowserWindowForProfile(profile_),
770 CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
771 #endif // defined(ENABLE_BACKGROUND)
774 // Helper methods --------------------------------------------------------------
776 // Assumes user_visible always since this is just meant to check
777 // if the permission was previously granted and not revoked.
778 bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
779 return GetPermissionStatus(origin, origin, true /* user_visible */) ==
780 blink::WebPushPermissionStatusGranted;
783 bool PushMessagingServiceImpl::AreMessagePayloadsEnabled() const {
784 return base::CommandLine::ForCurrentProcess()->HasSwitch(
785 switches::kEnablePushMessagePayload);
788 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
789 gcm::GCMProfileService* gcm_profile_service =
790 gcm::GCMProfileServiceFactory::GetForProfile(profile_);
791 CHECK(gcm_profile_service);
792 CHECK(gcm_profile_service->driver());
793 return gcm_profile_service->driver();