Don't compile save_password_infobar_delegate.cc everywhere but Mac and Android
[chromium-blink-merge.git] / chrome / browser / push_messaging / push_messaging_service_impl.cc
blob229057dedb75747b1169362206170228ba4a6c9e
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 // Note: if push is ever enabled in incognito, the background mode integration
88 // should not be enabled for it.
89 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
90 if (command_line->HasSwitch(switches::kDisablePushApiBackgroundMode))
91 return false;
92 return command_line->HasSwitch(switches::kEnablePushApiBackgroundMode);
94 #endif // defined(ENABLE_BACKGROUND)
96 } // namespace
98 // static
99 void PushMessagingServiceImpl::RegisterProfilePrefs(
100 user_prefs::PrefRegistrySyncable* registry) {
101 registry->RegisterIntegerPref(prefs::kPushMessagingRegistrationCount, 0);
102 PushMessagingAppIdentifier::RegisterProfilePrefs(registry);
105 // static
106 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
107 // TODO(johnme): Consider whether push should be enabled in incognito.
108 if (!profile || profile->IsOffTheRecord())
109 return;
111 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
112 // pref), be careful that this pref is read from the right profile, as prefs
113 // defined in a regular profile are visible in the corresponding incognito
114 // profile unless overridden.
115 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
116 int count = profile->GetPrefs()->GetInteger(
117 prefs::kPushMessagingRegistrationCount);
118 if (count <= 0)
119 return;
121 PushMessagingServiceImpl* push_service =
122 PushMessagingServiceFactory::GetForProfile(profile);
123 push_service->IncreasePushSubscriptionCount(count, false /* is_pending */);
126 PushMessagingServiceImpl::PushMessagingServiceImpl(Profile* profile)
127 : profile_(profile),
128 push_subscription_count_(0),
129 pending_push_subscription_count_(0),
130 #if defined(ENABLE_NOTIFICATIONS)
131 notification_manager_(profile),
132 #endif
133 weak_factory_(this) {
134 DCHECK(profile);
135 HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(this);
138 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
141 void PushMessagingServiceImpl::IncreasePushSubscriptionCount(int add,
142 bool is_pending) {
143 DCHECK(add > 0);
144 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
145 GetGCMDriver()->AddAppHandler(kPushMessagingAppIdentifierPrefix, this);
147 if (is_pending) {
148 pending_push_subscription_count_ += add;
149 } else {
150 #if defined(ENABLE_BACKGROUND)
151 if (UseBackgroundMode() && g_browser_process->background_mode_manager() &&
152 !push_subscription_count_) {
153 g_browser_process->background_mode_manager()->RegisterTrigger(
154 profile_, this, false /* should_notify_user */);
156 #endif // defined(ENABLE_BACKGROUND)
157 push_subscription_count_ += add;
158 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
159 push_subscription_count_);
163 void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
164 bool was_pending) {
165 DCHECK(subtract > 0);
166 if (was_pending) {
167 pending_push_subscription_count_ -= subtract;
168 DCHECK(pending_push_subscription_count_ >= 0);
169 } else {
170 push_subscription_count_ -= subtract;
171 DCHECK(push_subscription_count_ >= 0);
172 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
173 push_subscription_count_);
175 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
176 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
178 #if defined(ENABLE_BACKGROUND)
179 if (UseBackgroundMode() && g_browser_process->background_mode_manager()) {
180 g_browser_process->background_mode_manager()->UnregisterTrigger(profile_,
181 this);
183 #endif // defined(ENABLE_BACKGROUND)
187 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
188 return !PushMessagingAppIdentifier::FindByAppId(profile_, app_id).is_null();
191 void PushMessagingServiceImpl::ShutdownHandler() {
192 // Shutdown() should come before and it removes us from the list of app
193 // handlers of gcm::GCMDriver so this shouldn't ever been called.
194 NOTREACHED();
197 // OnMessage methods -----------------------------------------------------------
199 void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
200 const gcm::IncomingMessage& message) {
201 in_flight_message_deliveries_.insert(app_id);
203 base::Closure message_handled_closure =
204 message_callback_for_testing_.is_null() ? base::Bind(&base::DoNothing)
205 : message_callback_for_testing_;
206 PushMessagingAppIdentifier app_identifier =
207 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
208 // Drop message and unregister if app_id was unknown (maybe recently deleted).
209 if (app_identifier.is_null()) {
210 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message,
211 message_handled_closure,
212 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID);
213 return;
215 // Drop message and unregister if |origin| has lost push permission.
216 if (!IsPermissionSet(app_identifier.origin())) {
217 DeliverMessageCallback(app_id, app_identifier.origin(),
218 app_identifier.service_worker_registration_id(),
219 message, message_handled_closure,
220 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED);
221 return;
224 rappor::SampleDomainAndRegistryFromGURL(
225 g_browser_process->rappor_service(),
226 "PushMessaging.MessageReceived.Origin",
227 app_identifier.origin());
229 // The Push API only exposes a single string of data in the push event fired
230 // on the Service Worker. When developers send messages using GCM to the Push
231 // API and want to include a message payload, they must pass a single key-
232 // value pair, where the key is "data" and the value is the string they want
233 // to be passed to their Service Worker. For example, they could send the
234 // following JSON using the HTTPS GCM API:
235 // {
236 // "registration_ids": ["FOO", "BAR"],
237 // "data": {
238 // "data": "BAZ",
239 // },
240 // "delay_while_idle": true,
241 // }
242 // TODO(johnme): Make sure this is clearly documented for developers.
243 std::string data;
244 // TODO(peter): Message payloads are disabled pending mandatory encryption.
245 // https://crbug.com/449184
246 if (AreMessagePayloadsEnabled()) {
247 gcm::MessageData::const_iterator it = message.data.find("data");
248 if (it != message.data.end())
249 data = it->second;
252 content::BrowserContext::DeliverPushMessage(
253 profile_,
254 app_identifier.origin(),
255 app_identifier.service_worker_registration_id(),
256 data,
257 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
258 weak_factory_.GetWeakPtr(),
259 app_identifier.app_id(), app_identifier.origin(),
260 app_identifier.service_worker_registration_id(), message,
261 message_handled_closure));
264 void PushMessagingServiceImpl::DeliverMessageCallback(
265 const std::string& app_id,
266 const GURL& requesting_origin,
267 int64 service_worker_registration_id,
268 const gcm::IncomingMessage& message,
269 const base::Closure& message_handled_closure,
270 content::PushDeliveryStatus status) {
271 // Remove a single in-flight delivery for |app_id|. This has to be done using
272 // an iterator rather than by value, as the latter removes all entries.
273 DCHECK(in_flight_message_deliveries_.find(app_id) !=
274 in_flight_message_deliveries_.end());
276 in_flight_message_deliveries_.erase(
277 in_flight_message_deliveries_.find(app_id));
279 // TODO(mvanouwerkerk): Show a warning in the developer console of the
280 // Service Worker corresponding to app_id (and/or on an internals page).
281 // See https://crbug.com/508516 for options.
282 switch (status) {
283 // Call EnforceUserVisibleOnlyRequirements if the message was delivered to
284 // the Service Worker JavaScript, even if the website's event handler failed
285 // (to prevent sites deliberately failing in order to avoid having to show
286 // notifications).
287 case content::PUSH_DELIVERY_STATUS_SUCCESS:
288 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
289 #if defined(ENABLE_NOTIFICATIONS)
290 // Only enforce the user visible requirements after the entire queue of
291 // incoming messages for |app_id| has been flushed.
292 if (!in_flight_message_deliveries_.count(app_id)) {
293 notification_manager_.EnforceUserVisibleOnlyRequirements(
294 requesting_origin, service_worker_registration_id,
295 message_handled_closure);
296 } else {
297 message_handled_closure.Run();
299 #else
300 message_handled_closure.Run();
301 #endif
302 break;
303 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
304 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
305 message_handled_closure.Run();
306 break;
307 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
308 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
309 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
310 Unsubscribe(
311 app_id, message.sender_id,
312 base::Bind(&UnregisterCallbackToClosure, message_handled_closure));
313 break;
316 RecordDeliveryStatus(status);
319 void PushMessagingServiceImpl::SetMessageCallbackForTesting(
320 const base::Closure& callback) {
321 message_callback_for_testing_ = callback;
324 // Other gcm::GCMAppHandler methods -------------------------------------------
326 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
327 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
328 // corresponding to app_id.
331 void PushMessagingServiceImpl::OnSendError(
332 const std::string& app_id,
333 const gcm::GCMClient::SendErrorDetails& send_error_details) {
334 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
337 void PushMessagingServiceImpl::OnSendAcknowledged(
338 const std::string& app_id,
339 const std::string& message_id) {
340 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
343 // GetPushEndpoint method ------------------------------------------------------
345 GURL PushMessagingServiceImpl::GetPushEndpoint() {
346 return GURL(std::string(kPushMessagingEndpoint));
349 // Subscribe and GetPermissionStatus methods -----------------------------------
351 void PushMessagingServiceImpl::SubscribeFromDocument(
352 const GURL& requesting_origin,
353 int64 service_worker_registration_id,
354 const std::string& sender_id,
355 int renderer_id,
356 int render_frame_id,
357 bool user_visible,
358 const content::PushMessagingService::RegisterCallback& callback) {
359 PushMessagingAppIdentifier app_identifier =
360 PushMessagingAppIdentifier::Generate(requesting_origin,
361 service_worker_registration_id);
363 if (push_subscription_count_ + pending_push_subscription_count_ >=
364 kMaxRegistrations) {
365 SubscribeEndWithError(callback,
366 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
367 return;
370 content::RenderFrameHost* render_frame_host =
371 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
372 content::WebContents* web_contents =
373 content::WebContents::FromRenderFrameHost(render_frame_host);
374 if (!web_contents)
375 return;
377 if (!user_visible) {
378 web_contents->GetMainFrame()->AddMessageToConsole(
379 content::CONSOLE_MESSAGE_LEVEL_ERROR,
380 kSilentPushUnsupportedMessage);
382 SubscribeEndWithError(callback,
383 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
384 return;
387 // Push does not allow permission requests from iframes.
388 int request_id = -1;
390 profile_->GetPermissionManager()->RequestPermission(
391 content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(),
392 request_id, requesting_origin, true /* user_gesture */,
393 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
394 weak_factory_.GetWeakPtr(), app_identifier, sender_id,
395 callback));
398 void PushMessagingServiceImpl::SubscribeFromWorker(
399 const GURL& requesting_origin,
400 int64 service_worker_registration_id,
401 const std::string& sender_id,
402 bool user_visible,
403 const content::PushMessagingService::RegisterCallback& register_callback) {
404 PushMessagingAppIdentifier app_identifier =
405 PushMessagingAppIdentifier::Generate(requesting_origin,
406 service_worker_registration_id);
408 if (profile_->GetPrefs()->GetInteger(
409 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
410 SubscribeEndWithError(register_callback,
411 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
412 return;
415 GURL embedding_origin = requesting_origin;
416 blink::WebPushPermissionStatus permission_status =
417 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
418 embedding_origin,
419 user_visible);
420 if (permission_status != blink::WebPushPermissionStatusGranted) {
421 SubscribeEndWithError(register_callback,
422 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
423 return;
426 IncreasePushSubscriptionCount(1, true /* is_pending */);
427 std::vector<std::string> sender_ids(1, sender_id);
428 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
429 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
430 weak_factory_.GetWeakPtr(),
431 app_identifier, register_callback));
434 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
435 const GURL& requesting_origin,
436 const GURL& embedding_origin,
437 bool user_visible) {
438 if (!user_visible)
439 return blink::WebPushPermissionStatusDenied;
441 return ToPushPermission(profile_->GetPermissionManager()->GetPermissionStatus(
442 content::PermissionType::PUSH_MESSAGING, requesting_origin,
443 embedding_origin));
446 bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
447 return false;
450 void PushMessagingServiceImpl::SubscribeEnd(
451 const content::PushMessagingService::RegisterCallback& callback,
452 const std::string& subscription_id,
453 const std::vector<uint8_t>& curve25519dh,
454 content::PushRegistrationStatus status) {
455 callback.Run(subscription_id, curve25519dh, status);
458 void PushMessagingServiceImpl::SubscribeEndWithError(
459 const content::PushMessagingService::RegisterCallback& callback,
460 content::PushRegistrationStatus status) {
461 SubscribeEnd(callback, std::string() /* subscription_id */,
462 std::vector<uint8_t>() /* curve25519dh */, status);
465 void PushMessagingServiceImpl::DidSubscribe(
466 const PushMessagingAppIdentifier& app_identifier,
467 const content::PushMessagingService::RegisterCallback& callback,
468 const std::string& subscription_id,
469 gcm::GCMClient::Result result) {
470 DecreasePushSubscriptionCount(1, true /* was_pending */);
472 content::PushRegistrationStatus status =
473 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
475 switch (result) {
476 case gcm::GCMClient::SUCCESS:
477 // Do not get a certificate if message payloads have not been enabled.
478 if (!AreMessagePayloadsEnabled()) {
479 DidSubscribeWithPublicKey(
480 app_identifier, callback, subscription_id,
481 std::string() /* public_key */);
482 return;
485 // Make sure that this subscription has associated encryption keys prior
486 // to returning it to the developer - they'll need this information in
487 // order to send payloads to the user.
488 GetGCMDriver()->GetPublicKey(
489 app_identifier.app_id(),
490 base::Bind(
491 &PushMessagingServiceImpl::DidSubscribeWithPublicKey,
492 weak_factory_.GetWeakPtr(), app_identifier, callback,
493 subscription_id));
495 return;
496 case gcm::GCMClient::INVALID_PARAMETER:
497 case gcm::GCMClient::GCM_DISABLED:
498 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
499 case gcm::GCMClient::SERVER_ERROR:
500 case gcm::GCMClient::UNKNOWN_ERROR:
501 status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
502 break;
503 case gcm::GCMClient::NETWORK_ERROR:
504 case gcm::GCMClient::TTL_EXCEEDED:
505 status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
506 break;
509 SubscribeEndWithError(callback, status);
512 void PushMessagingServiceImpl::DidSubscribeWithPublicKey(
513 const PushMessagingAppIdentifier& app_identifier,
514 const content::PushMessagingService::RegisterCallback& callback,
515 const std::string& subscription_id,
516 const std::string& public_key) {
517 if (!public_key.size() && AreMessagePayloadsEnabled()) {
518 SubscribeEndWithError(
519 callback, content::PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE);
520 return;
523 app_identifier.PersistToPrefs(profile_);
525 IncreasePushSubscriptionCount(1, false /* is_pending */);
527 SubscribeEnd(callback, subscription_id,
528 std::vector<uint8_t>(public_key.begin(), public_key.end()),
529 content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE);
532 void PushMessagingServiceImpl::DidRequestPermission(
533 const PushMessagingAppIdentifier& app_identifier,
534 const std::string& sender_id,
535 const content::PushMessagingService::RegisterCallback& register_callback,
536 content::PermissionStatus permission_status) {
537 if (permission_status != content::PERMISSION_STATUS_GRANTED) {
538 SubscribeEndWithError(register_callback,
539 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
540 return;
543 IncreasePushSubscriptionCount(1, true /* is_pending */);
544 std::vector<std::string> sender_ids(1, sender_id);
545 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
546 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
547 weak_factory_.GetWeakPtr(),
548 app_identifier, register_callback));
551 // GetPublicEncryptionKey methods ----------------------------------------------
553 void PushMessagingServiceImpl::GetPublicEncryptionKey(
554 const GURL& origin,
555 int64_t service_worker_registration_id,
556 const PushMessagingService::PublicKeyCallback& callback) {
557 // An empty public key will be returned if payloads are not enabled.
558 if (!AreMessagePayloadsEnabled()) {
559 callback.Run(true /* success */, std::vector<uint8_t>());
560 return;
563 PushMessagingAppIdentifier app_identifier =
564 PushMessagingAppIdentifier::FindByServiceWorker(
565 profile_, origin, service_worker_registration_id);
567 DCHECK(!app_identifier.is_null());
569 GetGCMDriver()->GetPublicKey(
570 app_identifier.app_id(),
571 base::Bind(&PushMessagingServiceImpl::DidGetPublicKey,
572 weak_factory_.GetWeakPtr(), callback));
575 void PushMessagingServiceImpl::DidGetPublicKey(
576 const PushMessagingService::PublicKeyCallback& callback,
577 const std::string& public_key) const {
578 // I/O errors might prevent the GCM Driver from retrieving a key-pair.
579 const bool success = !!public_key.size();
581 callback.Run(success, std::vector<uint8_t>(public_key.begin(),
582 public_key.end()));
585 // Unsubscribe methods ---------------------------------------------------------
587 void PushMessagingServiceImpl::Unsubscribe(
588 const GURL& requesting_origin,
589 int64 service_worker_registration_id,
590 const std::string& sender_id,
591 const content::PushMessagingService::UnregisterCallback& callback) {
592 PushMessagingAppIdentifier app_identifier =
593 PushMessagingAppIdentifier::FindByServiceWorker(
594 profile_, requesting_origin, service_worker_registration_id);
595 if (app_identifier.is_null()) {
596 if (!callback.is_null()) {
597 callback.Run(
598 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
600 return;
603 Unsubscribe(app_identifier.app_id(), sender_id, callback);
606 void PushMessagingServiceImpl::Unsubscribe(
607 const std::string& app_id,
608 const std::string& sender_id,
609 const content::PushMessagingService::UnregisterCallback& callback) {
610 // Delete the mapping for this app_id, to guarantee that no messages get
611 // delivered in future (even if unregistration fails).
612 // TODO(johnme): Instead of deleting these app ids, store them elsewhere, and
613 // retry unregistration if it fails due to network errors (crbug.com/465399).
614 PushMessagingAppIdentifier app_identifier =
615 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
616 bool was_registered = !app_identifier.is_null();
617 if (was_registered)
618 app_identifier.DeleteFromPrefs(profile_);
620 const auto& unregister_callback =
621 base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
622 weak_factory_.GetWeakPtr(), was_registered, callback);
623 #if defined(OS_ANDROID)
624 // On Android the backend is different, and requires the original sender_id.
625 // UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
626 if (sender_id.empty())
627 unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
628 else
629 GetGCMDriver()->UnregisterWithSenderId(app_id, sender_id,
630 unregister_callback);
631 #else
632 GetGCMDriver()->Unregister(app_id, unregister_callback);
633 #endif
636 void PushMessagingServiceImpl::DidUnsubscribe(
637 bool was_subscribed,
638 const content::PushMessagingService::UnregisterCallback& callback,
639 gcm::GCMClient::Result result) {
640 if (was_subscribed)
641 DecreasePushSubscriptionCount(1, false /* was_pending */);
643 // Internal calls pass a null callback.
644 if (callback.is_null())
645 return;
647 if (!was_subscribed) {
648 callback.Run(
649 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
650 return;
652 switch (result) {
653 case gcm::GCMClient::SUCCESS:
654 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
655 break;
656 case gcm::GCMClient::INVALID_PARAMETER:
657 case gcm::GCMClient::GCM_DISABLED:
658 case gcm::GCMClient::SERVER_ERROR:
659 case gcm::GCMClient::UNKNOWN_ERROR:
660 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR);
661 break;
662 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
663 case gcm::GCMClient::NETWORK_ERROR:
664 case gcm::GCMClient::TTL_EXCEEDED:
665 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR);
666 break;
670 // OnContentSettingChanged methods ---------------------------------------------
672 void PushMessagingServiceImpl::OnContentSettingChanged(
673 const ContentSettingsPattern& primary_pattern,
674 const ContentSettingsPattern& secondary_pattern,
675 ContentSettingsType content_type,
676 std::string resource_identifier) {
677 if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
678 content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
679 return;
682 std::vector<PushMessagingAppIdentifier> all_app_identifiers =
683 PushMessagingAppIdentifier::GetAll(profile_);
685 base::Closure barrier_closure = base::BarrierClosure(
686 all_app_identifiers.size(),
687 content_setting_changed_callback_for_testing_.is_null()
688 ? base::Bind(&base::DoNothing)
689 : content_setting_changed_callback_for_testing_);
691 for (const PushMessagingAppIdentifier& app_identifier : all_app_identifiers) {
692 // If |primary_pattern| is not valid, we should always check for a
693 // permission change because it can happen for example when the entire
694 // Push or Notifications permissions are cleared.
695 // Otherwise, the permission should be checked if the pattern matches the
696 // origin.
697 if (primary_pattern.IsValid() &&
698 !primary_pattern.Matches(app_identifier.origin())) {
699 barrier_closure.Run();
700 continue;
703 if (IsPermissionSet(app_identifier.origin())) {
704 barrier_closure.Run();
705 continue;
708 GetSenderId(
709 profile_, app_identifier.origin(),
710 app_identifier.service_worker_registration_id(),
711 base::Bind(
712 &PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
713 weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
717 void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked(
718 const PushMessagingAppIdentifier& app_identifier,
719 const base::Closure& closure,
720 const std::string& sender_id,
721 bool success,
722 bool not_found) {
723 base::Closure barrier_closure = base::BarrierClosure(2, closure);
725 // Unsubscribe the PushMessagingAppIdentifier with the push service.
726 // It's possible for GetSenderId to have failed and sender_id to be empty, if
727 // cookies (and the SW database) for an origin got cleared before permissions
728 // are cleared for the origin. In that case Unsubscribe will just delete the
729 // app identifier to block future messages.
730 // TODO(johnme): Auto-unregister before SW DB is cleared
731 // (https://crbug.com/402458).
732 Unsubscribe(app_identifier.app_id(), sender_id,
733 base::Bind(&UnregisterCallbackToClosure, barrier_closure));
735 // Clear the associated service worker push registration id.
736 ClearPushSubscriptionID(profile_, app_identifier.origin(),
737 app_identifier.service_worker_registration_id(),
738 barrier_closure);
741 void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting(
742 const base::Closure& callback) {
743 content_setting_changed_callback_for_testing_ = callback;
746 // KeyedService methods -------------------------------------------------------
748 void PushMessagingServiceImpl::Shutdown() {
749 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
752 // BackgroundTrigger methods ---------------------------------------------------
753 base::string16 PushMessagingServiceImpl::GetName() {
754 return l10n_util::GetStringUTF16(IDS_NOTIFICATIONS_BACKGROUND_SERVICE_NAME);
757 gfx::ImageSkia* PushMessagingServiceImpl::GetIcon() {
758 return nullptr;
761 void PushMessagingServiceImpl::OnMenuClick() {
762 #if defined(ENABLE_BACKGROUND)
763 chrome::ShowContentSettings(
764 BackgroundModeManager::GetBrowserWindowForProfile(profile_),
765 CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
766 #endif // defined(ENABLE_BACKGROUND)
769 // Helper methods --------------------------------------------------------------
771 // Assumes user_visible always since this is just meant to check
772 // if the permission was previously granted and not revoked.
773 bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
774 return GetPermissionStatus(origin, origin, true /* user_visible */) ==
775 blink::WebPushPermissionStatusGranted;
778 bool PushMessagingServiceImpl::AreMessagePayloadsEnabled() const {
779 return base::CommandLine::ForCurrentProcess()->HasSwitch(
780 switches::kEnablePushMessagePayload);
783 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
784 gcm::GCMProfileService* gcm_profile_service =
785 gcm::GCMProfileServiceFactory::GetForProfile(profile_);
786 CHECK(gcm_profile_service);
787 CHECK(gcm_profile_service->driver());
788 return gcm_profile_service->driver();