Rename vector icon header files.
[chromium-blink-merge.git] / chrome / browser / push_messaging / push_messaging_service_impl.cc
blob10cb6bb7a8dd628f52a99d648433fdfb95ddd44f
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/permissions/permission_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
20 #include "chrome/browser/push_messaging/push_messaging_constants.h"
21 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
22 #include "chrome/browser/services/gcm/gcm_profile_service.h"
23 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h"
26 #include "components/content_settings/core/browser/host_content_settings_map.h"
27 #include "components/gcm_driver/gcm_driver.h"
28 #include "components/pref_registry/pref_registry_syncable.h"
29 #include "components/rappor/rappor_utils.h"
30 #include "content/public/browser/browser_context.h"
31 #include "content/public/browser/permission_type.h"
32 #include "content/public/browser/render_frame_host.h"
33 #include "content/public/browser/service_worker_context.h"
34 #include "content/public/browser/storage_partition.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/common/child_process_host.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/push_messaging_status.h"
40 namespace {
41 const int kMaxRegistrations = 1000000;
43 // Chrome does not yet support silent push messages, and requires websites to
44 // indicate that they will only send user-visible messages.
45 const char kSilentPushUnsupportedMessage[] =
46 "Chrome currently only supports the Push API for subscriptions that will "
47 "result in user-visible messages. You can indicate this by calling "
48 "pushManager.subscribe({userVisibleOnly: true}) instead. See "
49 "https://goo.gl/yqv4Q4 for more details.";
51 void RecordDeliveryStatus(content::PushDeliveryStatus status) {
52 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
53 status,
54 content::PUSH_DELIVERY_STATUS_LAST + 1);
57 blink::WebPushPermissionStatus ToPushPermission(
58 content::PermissionStatus permission_status) {
59 switch (permission_status) {
60 case content::PERMISSION_STATUS_GRANTED:
61 return blink::WebPushPermissionStatusGranted;
62 case content::PERMISSION_STATUS_DENIED:
63 return blink::WebPushPermissionStatusDenied;
64 case content::PERMISSION_STATUS_ASK:
65 return blink::WebPushPermissionStatusPrompt;
66 default:
67 NOTREACHED();
68 return blink::WebPushPermissionStatusDenied;
72 void UnregisterCallbackToClosure(
73 const base::Closure& closure, content::PushUnregistrationStatus status) {
74 closure.Run();
77 } // namespace
79 // static
80 void PushMessagingServiceImpl::RegisterProfilePrefs(
81 user_prefs::PrefRegistrySyncable* registry) {
82 registry->RegisterIntegerPref(prefs::kPushMessagingRegistrationCount, 0);
83 PushMessagingAppIdentifier::RegisterProfilePrefs(registry);
86 // static
87 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
88 // TODO(johnme): Consider whether push should be enabled in incognito.
89 if (!profile || profile->IsOffTheRecord())
90 return;
92 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
93 // pref), be careful that this pref is read from the right profile, as prefs
94 // defined in a regular profile are visible in the corresponding incognito
95 // profile unless overridden.
96 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
97 int count = profile->GetPrefs()->GetInteger(
98 prefs::kPushMessagingRegistrationCount);
99 if (count <= 0)
100 return;
102 PushMessagingServiceImpl* push_service =
103 PushMessagingServiceFactory::GetForProfile(profile);
104 push_service->IncreasePushSubscriptionCount(count, false /* is_pending */);
107 PushMessagingServiceImpl::PushMessagingServiceImpl(Profile* profile)
108 : profile_(profile),
109 push_subscription_count_(0),
110 pending_push_subscription_count_(0),
111 #if defined(ENABLE_NOTIFICATIONS)
112 notification_manager_(profile),
113 #endif
114 weak_factory_(this) {
115 DCHECK(profile);
116 profile_->GetHostContentSettingsMap()->AddObserver(this);
119 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
120 profile_->GetHostContentSettingsMap()->RemoveObserver(this);
123 void PushMessagingServiceImpl::IncreasePushSubscriptionCount(int add,
124 bool is_pending) {
125 DCHECK(add > 0);
126 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
127 GetGCMDriver()->AddAppHandler(kPushMessagingAppIdentifierPrefix, this);
129 if (is_pending) {
130 pending_push_subscription_count_ += add;
131 } else {
132 push_subscription_count_ += add;
133 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
134 push_subscription_count_);
138 void PushMessagingServiceImpl::DecreasePushSubscriptionCount(int subtract,
139 bool was_pending) {
140 DCHECK(subtract > 0);
141 if (was_pending) {
142 pending_push_subscription_count_ -= subtract;
143 DCHECK(pending_push_subscription_count_ >= 0);
144 } else {
145 push_subscription_count_ -= subtract;
146 DCHECK(push_subscription_count_ >= 0);
147 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
148 push_subscription_count_);
150 if (push_subscription_count_ + pending_push_subscription_count_ == 0) {
151 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
155 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
156 return !PushMessagingAppIdentifier::FindByAppId(profile_, app_id).is_null();
159 void PushMessagingServiceImpl::ShutdownHandler() {
160 // Shutdown() should come before and it removes us from the list of app
161 // handlers of gcm::GCMDriver so this shouldn't ever been called.
162 NOTREACHED();
165 // OnMessage methods -----------------------------------------------------------
167 void PushMessagingServiceImpl::OnMessage(const std::string& app_id,
168 const gcm::IncomingMessage& message) {
169 in_flight_message_deliveries_.insert(app_id);
171 base::Closure message_handled_closure =
172 message_callback_for_testing_.is_null() ? base::Bind(&base::DoNothing)
173 : message_callback_for_testing_;
174 PushMessagingAppIdentifier app_identifier =
175 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
176 // Drop message and unregister if app_id was unknown (maybe recently deleted).
177 if (app_identifier.is_null()) {
178 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message,
179 message_handled_closure,
180 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID);
181 return;
183 // Drop message and unregister if |origin| has lost push permission.
184 if (!IsPermissionSet(app_identifier.origin())) {
185 DeliverMessageCallback(app_id, app_identifier.origin(),
186 app_identifier.service_worker_registration_id(),
187 message, message_handled_closure,
188 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED);
189 return;
192 rappor::SampleDomainAndRegistryFromGURL(
193 g_browser_process->rappor_service(),
194 "PushMessaging.MessageReceived.Origin",
195 app_identifier.origin());
197 // The Push API only exposes a single string of data in the push event fired
198 // on the Service Worker. When developers send messages using GCM to the Push
199 // API and want to include a message payload, they must pass a single key-
200 // value pair, where the key is "data" and the value is the string they want
201 // to be passed to their Service Worker. For example, they could send the
202 // following JSON using the HTTPS GCM API:
203 // {
204 // "registration_ids": ["FOO", "BAR"],
205 // "data": {
206 // "data": "BAZ",
207 // },
208 // "delay_while_idle": true,
209 // }
210 // TODO(johnme): Make sure this is clearly documented for developers.
211 std::string data;
212 // TODO(peter): Message payloads are disabled pending mandatory encryption.
213 // https://crbug.com/449184
214 if (AreMessagePayloadsEnabled()) {
215 gcm::MessageData::const_iterator it = message.data.find("data");
216 if (it != message.data.end())
217 data = it->second;
220 content::BrowserContext::DeliverPushMessage(
221 profile_,
222 app_identifier.origin(),
223 app_identifier.service_worker_registration_id(),
224 data,
225 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
226 weak_factory_.GetWeakPtr(),
227 app_identifier.app_id(), app_identifier.origin(),
228 app_identifier.service_worker_registration_id(), message,
229 message_handled_closure));
232 void PushMessagingServiceImpl::DeliverMessageCallback(
233 const std::string& app_id,
234 const GURL& requesting_origin,
235 int64 service_worker_registration_id,
236 const gcm::IncomingMessage& message,
237 const base::Closure& message_handled_closure,
238 content::PushDeliveryStatus status) {
239 // Remove a single in-flight delivery for |app_id|. This has to be done using
240 // an iterator rather than by value, as the latter removes all entries.
241 DCHECK(in_flight_message_deliveries_.find(app_id) !=
242 in_flight_message_deliveries_.end());
244 in_flight_message_deliveries_.erase(
245 in_flight_message_deliveries_.find(app_id));
247 // TODO(mvanouwerkerk): Show a warning in the developer console of the
248 // Service Worker corresponding to app_id (and/or on an internals page).
249 // See https://crbug.com/508516 for options.
250 switch (status) {
251 // Call EnforceUserVisibleOnlyRequirements if the message was delivered to
252 // the Service Worker JavaScript, even if the website's event handler failed
253 // (to prevent sites deliberately failing in order to avoid having to show
254 // notifications).
255 case content::PUSH_DELIVERY_STATUS_SUCCESS:
256 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
257 #if defined(ENABLE_NOTIFICATIONS)
258 // Only enforce the user visible requirements after the entire queue of
259 // incoming messages for |app_id| has been flushed.
260 if (!in_flight_message_deliveries_.count(app_id)) {
261 notification_manager_.EnforceUserVisibleOnlyRequirements(
262 requesting_origin, service_worker_registration_id,
263 message_handled_closure);
264 } else {
265 message_handled_closure.Run();
267 #else
268 message_handled_closure.Run();
269 #endif
270 break;
271 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
272 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
273 message_handled_closure.Run();
274 break;
275 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
276 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
277 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
278 Unsubscribe(
279 app_id, message.sender_id,
280 base::Bind(&UnregisterCallbackToClosure, message_handled_closure));
281 break;
284 RecordDeliveryStatus(status);
287 void PushMessagingServiceImpl::SetMessageCallbackForTesting(
288 const base::Closure& callback) {
289 message_callback_for_testing_ = callback;
292 // Other gcm::GCMAppHandler methods -------------------------------------------
294 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
295 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
296 // corresponding to app_id.
299 void PushMessagingServiceImpl::OnSendError(
300 const std::string& app_id,
301 const gcm::GCMClient::SendErrorDetails& send_error_details) {
302 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
305 void PushMessagingServiceImpl::OnSendAcknowledged(
306 const std::string& app_id,
307 const std::string& message_id) {
308 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
311 // GetPushEndpoint method ------------------------------------------------------
313 GURL PushMessagingServiceImpl::GetPushEndpoint() {
314 return GURL(std::string(kPushMessagingEndpoint));
317 // Subscribe and GetPermissionStatus methods -----------------------------------
319 void PushMessagingServiceImpl::SubscribeFromDocument(
320 const GURL& requesting_origin,
321 int64 service_worker_registration_id,
322 const std::string& sender_id,
323 int renderer_id,
324 int render_frame_id,
325 bool user_visible,
326 const content::PushMessagingService::RegisterCallback& callback) {
327 PushMessagingAppIdentifier app_identifier =
328 PushMessagingAppIdentifier::Generate(requesting_origin,
329 service_worker_registration_id);
331 if (push_subscription_count_ + pending_push_subscription_count_ >=
332 kMaxRegistrations) {
333 SubscribeEndWithError(callback,
334 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
335 return;
338 content::RenderFrameHost* render_frame_host =
339 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
340 content::WebContents* web_contents =
341 content::WebContents::FromRenderFrameHost(render_frame_host);
342 if (!web_contents)
343 return;
345 if (!user_visible) {
346 web_contents->GetMainFrame()->AddMessageToConsole(
347 content::CONSOLE_MESSAGE_LEVEL_ERROR,
348 kSilentPushUnsupportedMessage);
350 SubscribeEndWithError(callback,
351 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
352 return;
355 // Push does not allow permission requests from iframes.
356 int request_id = -1;
358 profile_->GetPermissionManager()->RequestPermission(
359 content::PermissionType::PUSH_MESSAGING, web_contents->GetMainFrame(),
360 request_id, requesting_origin, true /* user_gesture */,
361 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
362 weak_factory_.GetWeakPtr(), app_identifier, sender_id,
363 callback));
366 void PushMessagingServiceImpl::SubscribeFromWorker(
367 const GURL& requesting_origin,
368 int64 service_worker_registration_id,
369 const std::string& sender_id,
370 bool user_visible,
371 const content::PushMessagingService::RegisterCallback& register_callback) {
372 PushMessagingAppIdentifier app_identifier =
373 PushMessagingAppIdentifier::Generate(requesting_origin,
374 service_worker_registration_id);
376 if (profile_->GetPrefs()->GetInteger(
377 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
378 SubscribeEndWithError(register_callback,
379 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
380 return;
383 GURL embedding_origin = requesting_origin;
384 blink::WebPushPermissionStatus permission_status =
385 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
386 embedding_origin,
387 user_visible);
388 if (permission_status != blink::WebPushPermissionStatusGranted) {
389 SubscribeEndWithError(register_callback,
390 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
391 return;
394 IncreasePushSubscriptionCount(1, true /* is_pending */);
395 std::vector<std::string> sender_ids(1, sender_id);
396 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
397 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
398 weak_factory_.GetWeakPtr(),
399 app_identifier, register_callback));
402 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
403 const GURL& requesting_origin,
404 const GURL& embedding_origin,
405 bool user_visible) {
406 if (!user_visible)
407 return blink::WebPushPermissionStatusDenied;
409 return ToPushPermission(profile_->GetPermissionManager()->GetPermissionStatus(
410 content::PermissionType::PUSH_MESSAGING, requesting_origin,
411 embedding_origin));
414 bool PushMessagingServiceImpl::SupportNonVisibleMessages() {
415 return false;
418 void PushMessagingServiceImpl::SubscribeEnd(
419 const content::PushMessagingService::RegisterCallback& callback,
420 const std::string& subscription_id,
421 const std::vector<uint8_t>& curve25519dh,
422 content::PushRegistrationStatus status) {
423 callback.Run(subscription_id, curve25519dh, status);
426 void PushMessagingServiceImpl::SubscribeEndWithError(
427 const content::PushMessagingService::RegisterCallback& callback,
428 content::PushRegistrationStatus status) {
429 SubscribeEnd(callback, std::string() /* subscription_id */,
430 std::vector<uint8_t>() /* curve25519dh */, status);
433 void PushMessagingServiceImpl::DidSubscribe(
434 const PushMessagingAppIdentifier& app_identifier,
435 const content::PushMessagingService::RegisterCallback& callback,
436 const std::string& subscription_id,
437 gcm::GCMClient::Result result) {
438 DecreasePushSubscriptionCount(1, true /* was_pending */);
440 content::PushRegistrationStatus status =
441 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
443 switch (result) {
444 case gcm::GCMClient::SUCCESS:
445 // Do not get a certificate if message payloads have not been enabled.
446 if (!AreMessagePayloadsEnabled()) {
447 DidSubscribeWithPublicKey(
448 app_identifier, callback, subscription_id,
449 std::string() /* public_key */);
450 return;
453 // Make sure that this subscription has associated encryption keys prior
454 // to returning it to the developer - they'll need this information in
455 // order to send payloads to the user.
456 GetGCMDriver()->GetPublicKey(
457 app_identifier.app_id(),
458 base::Bind(
459 &PushMessagingServiceImpl::DidSubscribeWithPublicKey,
460 weak_factory_.GetWeakPtr(), app_identifier, callback,
461 subscription_id));
463 return;
464 case gcm::GCMClient::INVALID_PARAMETER:
465 case gcm::GCMClient::GCM_DISABLED:
466 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
467 case gcm::GCMClient::SERVER_ERROR:
468 case gcm::GCMClient::UNKNOWN_ERROR:
469 status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
470 break;
471 case gcm::GCMClient::NETWORK_ERROR:
472 case gcm::GCMClient::TTL_EXCEEDED:
473 status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
474 break;
477 SubscribeEndWithError(callback, status);
480 void PushMessagingServiceImpl::DidSubscribeWithPublicKey(
481 const PushMessagingAppIdentifier& app_identifier,
482 const content::PushMessagingService::RegisterCallback& callback,
483 const std::string& subscription_id,
484 const std::string& public_key) {
485 if (!public_key.size() && AreMessagePayloadsEnabled()) {
486 SubscribeEndWithError(
487 callback, content::PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE);
488 return;
491 app_identifier.PersistToPrefs(profile_);
493 IncreasePushSubscriptionCount(1, false /* is_pending */);
495 SubscribeEnd(callback, subscription_id,
496 std::vector<uint8_t>(public_key.begin(), public_key.end()),
497 content::PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE);
500 void PushMessagingServiceImpl::DidRequestPermission(
501 const PushMessagingAppIdentifier& app_identifier,
502 const std::string& sender_id,
503 const content::PushMessagingService::RegisterCallback& register_callback,
504 content::PermissionStatus permission_status) {
505 if (permission_status != content::PERMISSION_STATUS_GRANTED) {
506 SubscribeEndWithError(register_callback,
507 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
508 return;
511 IncreasePushSubscriptionCount(1, true /* is_pending */);
512 std::vector<std::string> sender_ids(1, sender_id);
513 GetGCMDriver()->Register(app_identifier.app_id(), sender_ids,
514 base::Bind(&PushMessagingServiceImpl::DidSubscribe,
515 weak_factory_.GetWeakPtr(),
516 app_identifier, register_callback));
519 // GetPublicEncryptionKey methods ----------------------------------------------
521 void PushMessagingServiceImpl::GetPublicEncryptionKey(
522 const GURL& origin,
523 int64_t service_worker_registration_id,
524 const PushMessagingService::PublicKeyCallback& callback) {
525 // An empty public key will be returned if payloads are not enabled.
526 if (!AreMessagePayloadsEnabled()) {
527 callback.Run(true /* success */, std::vector<uint8_t>());
528 return;
531 PushMessagingAppIdentifier app_identifier =
532 PushMessagingAppIdentifier::FindByServiceWorker(
533 profile_, origin, service_worker_registration_id);
535 DCHECK(!app_identifier.is_null());
537 GetGCMDriver()->GetPublicKey(
538 app_identifier.app_id(),
539 base::Bind(&PushMessagingServiceImpl::DidGetPublicKey,
540 weak_factory_.GetWeakPtr(), callback));
543 void PushMessagingServiceImpl::DidGetPublicKey(
544 const PushMessagingService::PublicKeyCallback& callback,
545 const std::string& public_key) const {
546 // I/O errors might prevent the GCM Driver from retrieving a key-pair.
547 const bool success = !!public_key.size();
549 callback.Run(success, std::vector<uint8_t>(public_key.begin(),
550 public_key.end()));
553 // Unsubscribe methods ---------------------------------------------------------
555 void PushMessagingServiceImpl::Unsubscribe(
556 const GURL& requesting_origin,
557 int64 service_worker_registration_id,
558 const std::string& sender_id,
559 const content::PushMessagingService::UnregisterCallback& callback) {
560 PushMessagingAppIdentifier app_identifier =
561 PushMessagingAppIdentifier::FindByServiceWorker(
562 profile_, requesting_origin, service_worker_registration_id);
563 if (app_identifier.is_null()) {
564 if (!callback.is_null()) {
565 callback.Run(
566 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
568 return;
571 Unsubscribe(app_identifier.app_id(), sender_id, callback);
574 void PushMessagingServiceImpl::Unsubscribe(
575 const std::string& app_id,
576 const std::string& sender_id,
577 const content::PushMessagingService::UnregisterCallback& callback) {
578 // Delete the mapping for this app_id, to guarantee that no messages get
579 // delivered in future (even if unregistration fails).
580 // TODO(johnme): Instead of deleting these app ids, store them elsewhere, and
581 // retry unregistration if it fails due to network errors (crbug.com/465399).
582 PushMessagingAppIdentifier app_identifier =
583 PushMessagingAppIdentifier::FindByAppId(profile_, app_id);
584 bool was_registered = !app_identifier.is_null();
585 if (was_registered)
586 app_identifier.DeleteFromPrefs(profile_);
588 const auto& unregister_callback =
589 base::Bind(&PushMessagingServiceImpl::DidUnsubscribe,
590 weak_factory_.GetWeakPtr(), was_registered, callback);
591 #if defined(OS_ANDROID)
592 // On Android the backend is different, and requires the original sender_id.
593 // UnsubscribeBecausePermissionRevoked sometimes calls us with an empty one.
594 if (sender_id.empty())
595 unregister_callback.Run(gcm::GCMClient::INVALID_PARAMETER);
596 else
597 GetGCMDriver()->UnregisterWithSenderId(app_id, sender_id,
598 unregister_callback);
599 #else
600 GetGCMDriver()->Unregister(app_id, unregister_callback);
601 #endif
604 void PushMessagingServiceImpl::DidUnsubscribe(
605 bool was_subscribed,
606 const content::PushMessagingService::UnregisterCallback& callback,
607 gcm::GCMClient::Result result) {
608 if (was_subscribed)
609 DecreasePushSubscriptionCount(1, false /* was_pending */);
611 // Internal calls pass a null callback.
612 if (callback.is_null())
613 return;
615 if (!was_subscribed) {
616 callback.Run(
617 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
618 return;
620 switch (result) {
621 case gcm::GCMClient::SUCCESS:
622 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
623 break;
624 case gcm::GCMClient::INVALID_PARAMETER:
625 case gcm::GCMClient::GCM_DISABLED:
626 case gcm::GCMClient::SERVER_ERROR:
627 case gcm::GCMClient::UNKNOWN_ERROR:
628 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR);
629 break;
630 case gcm::GCMClient::ASYNC_OPERATION_PENDING:
631 case gcm::GCMClient::NETWORK_ERROR:
632 case gcm::GCMClient::TTL_EXCEEDED:
633 callback.Run(content::PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR);
634 break;
638 // OnContentSettingChanged methods ---------------------------------------------
640 void PushMessagingServiceImpl::OnContentSettingChanged(
641 const ContentSettingsPattern& primary_pattern,
642 const ContentSettingsPattern& secondary_pattern,
643 ContentSettingsType content_type,
644 std::string resource_identifier) {
645 if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
646 content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
647 return;
650 std::vector<PushMessagingAppIdentifier> all_app_identifiers =
651 PushMessagingAppIdentifier::GetAll(profile_);
653 base::Closure barrier_closure = base::BarrierClosure(
654 all_app_identifiers.size(),
655 content_setting_changed_callback_for_testing_.is_null()
656 ? base::Bind(&base::DoNothing)
657 : content_setting_changed_callback_for_testing_);
659 for (const PushMessagingAppIdentifier& app_identifier : all_app_identifiers) {
660 // If |primary_pattern| is not valid, we should always check for a
661 // permission change because it can happen for example when the entire
662 // Push or Notifications permissions are cleared.
663 // Otherwise, the permission should be checked if the pattern matches the
664 // origin.
665 if (primary_pattern.IsValid() &&
666 !primary_pattern.Matches(app_identifier.origin())) {
667 barrier_closure.Run();
668 continue;
671 if (IsPermissionSet(app_identifier.origin())) {
672 barrier_closure.Run();
673 continue;
676 GetSenderId(
677 profile_, app_identifier.origin(),
678 app_identifier.service_worker_registration_id(),
679 base::Bind(
680 &PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked,
681 weak_factory_.GetWeakPtr(), app_identifier, barrier_closure));
685 void PushMessagingServiceImpl::UnsubscribeBecausePermissionRevoked(
686 const PushMessagingAppIdentifier& app_identifier,
687 const base::Closure& closure,
688 const std::string& sender_id,
689 bool success,
690 bool not_found) {
691 base::Closure barrier_closure = base::BarrierClosure(2, closure);
693 // Unsubscribe the PushMessagingAppIdentifier with the push service.
694 // It's possible for GetSenderId to have failed and sender_id to be empty, if
695 // cookies (and the SW database) for an origin got cleared before permissions
696 // are cleared for the origin. In that case Unsubscribe will just delete the
697 // app identifier to block future messages.
698 // TODO(johnme): Auto-unregister before SW DB is cleared
699 // (https://crbug.com/402458).
700 Unsubscribe(app_identifier.app_id(), sender_id,
701 base::Bind(&UnregisterCallbackToClosure, barrier_closure));
703 // Clear the associated service worker push registration id.
704 ClearPushSubscriptionID(profile_, app_identifier.origin(),
705 app_identifier.service_worker_registration_id(),
706 barrier_closure);
709 void PushMessagingServiceImpl::SetContentSettingChangedCallbackForTesting(
710 const base::Closure& callback) {
711 content_setting_changed_callback_for_testing_ = callback;
714 // KeyedService methods -------------------------------------------------------
716 void PushMessagingServiceImpl::Shutdown() {
717 GetGCMDriver()->RemoveAppHandler(kPushMessagingAppIdentifierPrefix);
720 // Helper methods --------------------------------------------------------------
722 // Assumes user_visible always since this is just meant to check
723 // if the permission was previously granted and not revoked.
724 bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
725 return GetPermissionStatus(origin, origin, true /* user_visible */) ==
726 blink::WebPushPermissionStatusGranted;
729 bool PushMessagingServiceImpl::AreMessagePayloadsEnabled() const {
730 return base::CommandLine::ForCurrentProcess()->HasSwitch(
731 switches::kEnablePushMessagePayload);
734 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
735 gcm::GCMProfileService* gcm_profile_service =
736 gcm::GCMProfileServiceFactory::GetForProfile(profile_);
737 CHECK(gcm_profile_service);
738 CHECK(gcm_profile_service->driver());
739 return gcm_profile_service->driver();