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