Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: chrome/browser/services/gcm/push_messaging_service_impl.cc

Issue 955673004: Move gcm-independent parts of push messaging out of gcm namespace and directory (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: remove redundant cast Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/services/gcm/push_messaging_service_impl.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h"
6
7 #include <bitset>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/notifications/notification_ui_manager.h"
19 #include "chrome/browser/notifications/platform_notification_service_impl.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/services/gcm/gcm_profile_service.h"
22 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
23 #include "chrome/browser/services/gcm/push_messaging_application_id.h"
24 #include "chrome/browser/services/gcm/push_messaging_constants.h"
25 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
26 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory. 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/content_settings/core/common/permission_request_id.h"
32 #include "components/gcm_driver/gcm_driver.h"
33 #include "components/pref_registry/pref_registry_syncable.h"
34 #include "content/public/browser/browser_context.h"
35 #include "content/public/browser/browser_thread.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/platform_notification_data.h"
43 #include "content/public/common/push_messaging_status.h"
44 #include "third_party/skia/include/core/SkBitmap.h"
45 #include "ui/base/l10n/l10n_util.h"
46
47 #if defined(OS_ANDROID)
48 #include "chrome/browser/ui/android/tab_model/tab_model.h"
49 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
50 #else
51 #include "chrome/browser/ui/browser.h"
52 #include "chrome/browser/ui/browser_iterator.h"
53 #include "chrome/browser/ui/tabs/tab_strip_model.h"
54 #endif
55
56 namespace gcm {
57
58 namespace {
59 const int kMaxRegistrations = 1000000;
60
61 void RecordDeliveryStatus(content::PushDeliveryStatus status) {
62 UMA_HISTOGRAM_ENUMERATION("PushMessaging.DeliveryStatus",
63 status,
64 content::PUSH_DELIVERY_STATUS_LAST + 1);
65 }
66
67 void RecordUserVisibleStatus(content::PushUserVisibleStatus status) {
68 UMA_HISTOGRAM_ENUMERATION("PushMessaging.UserVisibleStatus",
69 status,
70 content::PUSH_USER_VISIBLE_STATUS_LAST + 1);
71 }
72
73 blink::WebPushPermissionStatus ToPushPermission(ContentSetting setting) {
74 switch (setting) {
75 case CONTENT_SETTING_ALLOW:
76 return blink::WebPushPermissionStatusGranted;
77 case CONTENT_SETTING_BLOCK:
78 return blink::WebPushPermissionStatusDenied;
79 case CONTENT_SETTING_ASK:
80 return blink::WebPushPermissionStatusDefault;
81 default:
82 NOTREACHED();
83 return blink::WebPushPermissionStatusDenied;
84 }
85 }
86
87 } // namespace
88
89 // static
90 void PushMessagingServiceImpl::RegisterProfilePrefs(
91 user_prefs::PrefRegistrySyncable* registry) {
92 registry->RegisterIntegerPref(
93 prefs::kPushMessagingRegistrationCount,
94 0,
95 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
96 PushMessagingApplicationId::RegisterProfilePrefs(registry);
97 }
98
99 // static
100 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
101 // TODO(johnme): Consider whether push should be enabled in incognito.
102 if (!profile || profile->IsOffTheRecord())
103 return;
104
105 // TODO(johnme): If push becomes enabled in incognito (and this still uses a
106 // pref), be careful that this pref is read from the right profile, as prefs
107 // defined in a regular profile are visible in the corresponding incognito
108 // profile unless overridden.
109 // TODO(johnme): Make sure this pref doesn't get out of sync after crashes.
110 int count = profile->GetPrefs()->GetInteger(
111 prefs::kPushMessagingRegistrationCount);
112 if (count <= 0)
113 return;
114
115 // Create the GCMProfileService, and hence instantiate this class.
116 GCMProfileService* gcm_service =
117 GCMProfileServiceFactory::GetForProfile(profile);
118 PushMessagingServiceImpl* push_service =
119 static_cast<PushMessagingServiceImpl*>(
120 gcm_service->push_messaging_service());
121
122 push_service->IncreasePushRegistrationCount(count, false /* is_pending */);
123 }
124
125 PushMessagingServiceImpl::PushMessagingServiceImpl(
126 GCMProfileService* gcm_profile_service,
127 Profile* profile)
128 : gcm_profile_service_(gcm_profile_service),
129 profile_(profile),
130 push_registration_count_(0),
131 pending_push_registration_count_(0),
132 weak_factory_(this) {
133 // In some tests, we might end up with |profile_| being null at this point.
134 // When that is the case |profile_| will be set in SetProfileForTesting(), at
135 // which point the service will start to observe HostContentSettingsMap.
136 if (profile_)
137 profile_->GetHostContentSettingsMap()->AddObserver(this);
138 }
139
140 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
141 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
142 // then we should call RemoveAppHandler.
143 profile_->GetHostContentSettingsMap()->RemoveObserver(this);
144 }
145
146 void PushMessagingServiceImpl::IncreasePushRegistrationCount(int add,
147 bool is_pending) {
148 DCHECK(add > 0);
149 if (push_registration_count_ + pending_push_registration_count_ == 0) {
150 gcm_profile_service_->driver()->AddAppHandler(
151 kPushMessagingApplicationIdPrefix, this);
152 }
153 if (is_pending) {
154 pending_push_registration_count_ += add;
155 } else {
156 push_registration_count_ += add;
157 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
158 push_registration_count_);
159 }
160 }
161
162 void PushMessagingServiceImpl::DecreasePushRegistrationCount(int subtract,
163 bool was_pending) {
164 DCHECK(subtract > 0);
165 if (was_pending) {
166 pending_push_registration_count_ -= subtract;
167 DCHECK(pending_push_registration_count_ >= 0);
168 } else {
169 push_registration_count_ -= subtract;
170 DCHECK(push_registration_count_ >= 0);
171 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
172 push_registration_count_);
173 }
174 if (push_registration_count_ + pending_push_registration_count_ == 0) {
175 gcm_profile_service_->driver()->RemoveAppHandler(
176 kPushMessagingApplicationIdPrefix);
177 }
178 }
179
180 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
181 return PushMessagingApplicationId::Get(profile_, app_id).IsValid();
182 }
183
184 void PushMessagingServiceImpl::ShutdownHandler() {
185 // TODO(johnme): Do any necessary cleanup.
186 }
187
188 // OnMessage methods -----------------------------------------------------------
189
190 void PushMessagingServiceImpl::OnMessage(
191 const std::string& app_id,
192 const GCMClient::IncomingMessage& message) {
193 PushMessagingApplicationId application_id =
194 PushMessagingApplicationId::Get(profile_, app_id);
195 // Drop message and unregister if app id was unknown (maybe recently deleted).
196 if (!application_id.IsValid()) {
197 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message,
198 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID);
199 return;
200 }
201 // Drop message and unregister if |origin| has lost push permission.
202 if (!HasPermission(application_id.origin())) {
203 DeliverMessageCallback(app_id, application_id.origin(),
204 application_id.service_worker_registration_id(),
205 message,
206 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED);
207 return;
208 }
209
210 // The Push API only exposes a single string of data in the push event fired
211 // on the Service Worker. When developers send messages using GCM to the Push
212 // API and want to include a message payload, they must pass a single key-
213 // value pair, where the key is "data" and the value is the string they want
214 // to be passed to their Service Worker. For example, they could send the
215 // following JSON using the HTTPS GCM API:
216 // {
217 // "registration_ids": ["FOO", "BAR"],
218 // "data": {
219 // "data": "BAZ",
220 // },
221 // "delay_while_idle": true,
222 // }
223 // TODO(johnme): Make sure this is clearly documented for developers.
224 std::string data;
225 // TODO(peter): Message payloads are disabled pending mandatory encryption.
226 // https://crbug.com/449184
227 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
228 switches::kEnablePushMessagePayload)) {
229 GCMClient::MessageData::const_iterator it = message.data.find("data");
230 if (it != message.data.end())
231 data = it->second;
232 }
233
234 content::BrowserContext::DeliverPushMessage(
235 profile_,
236 application_id.origin(),
237 application_id.service_worker_registration_id(),
238 data,
239 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
240 weak_factory_.GetWeakPtr(),
241 application_id.app_id_guid(), application_id.origin(),
242 application_id.service_worker_registration_id(), message));
243 }
244
245 void PushMessagingServiceImpl::DeliverMessageCallback(
246 const std::string& app_id_guid,
247 const GURL& requesting_origin,
248 int64 service_worker_registration_id,
249 const GCMClient::IncomingMessage& message,
250 content::PushDeliveryStatus status) {
251 // TODO(mvanouwerkerk): Show a warning in the developer console of the
252 // Service Worker corresponding to app_id (and/or on an internals page).
253 // TODO(mvanouwerkerk): Is there a way to recover from failure?
254 switch (status) {
255 // Call RequireUserVisibleUX if the message was delivered to the Service
256 // Worker JS, even if the website's event handler failed (to prevent sites
257 // deliberately failing in order to avoid having to show notifications).
258 case content::PUSH_DELIVERY_STATUS_SUCCESS:
259 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
260 RequireUserVisibleUX(requesting_origin, service_worker_registration_id);
261 break;
262 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
263 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
264 break;
265 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID:
266 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED:
267 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
268 Unregister(app_id_guid, message.sender_id, true /* retry_on_failure */,
269 UnregisterCallback());
270 break;
271 }
272 RecordDeliveryStatus(status);
273 }
274
275 void PushMessagingServiceImpl::RequireUserVisibleUX(
276 const GURL& requesting_origin, int64 service_worker_registration_id) {
277 #if defined(ENABLE_NOTIFICATIONS)
278 // TODO(johnme): Relax this heuristic slightly.
279 PlatformNotificationServiceImpl* notification_service =
280 PlatformNotificationServiceImpl::GetInstance();
281 // Can't use g_browser_process->notification_ui_manager(), since the test uses
282 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting.
283 // TODO(peter): Remove the need to use both APIs here once Notification.get()
284 // is supported.
285 int notification_count = notification_service->GetNotificationUIManager()->
286 GetAllIdsByProfileAndSourceOrigin(profile_, requesting_origin).size();
287 // TODO(johnme): Hiding an existing notification should also count as a useful
288 // user-visible action done in response to a push message - but make sure that
289 // sending two messages in rapid succession which show then hide a
290 // notification doesn't count.
291 bool notification_shown = notification_count > 0;
292
293 bool notification_needed = true;
294 // Sites with a currently visible tab don't need to show notifications.
295 #if defined(OS_ANDROID)
296 for (auto it = TabModelList::begin(); it != TabModelList::end(); ++it) {
297 Profile* profile = (*it)->GetProfile();
298 content::WebContents* active_web_contents =
299 (*it)->GetActiveWebContents();
300 #else
301 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
302 Profile* profile = it->profile();
303 content::WebContents* active_web_contents =
304 it->tab_strip_model()->GetActiveWebContents();
305 #endif
306 if (!active_web_contents || !active_web_contents->GetMainFrame())
307 continue;
308
309 // Don't leak information from other profiles.
310 if (profile != profile_)
311 continue;
312
313 // Ignore minimized windows etc.
314 switch (active_web_contents->GetMainFrame()->GetVisibilityState()) {
315 case blink::WebPageVisibilityStateHidden:
316 case blink::WebPageVisibilityStatePrerender:
317 continue;
318 case blink::WebPageVisibilityStateVisible:
319 break;
320 }
321
322 // Use the visible URL since that's the one the user is aware of (and it
323 // doesn't matter whether the page loaded successfully).
324 const GURL& active_url = active_web_contents->GetVisibleURL();
325 if (requesting_origin == active_url.GetOrigin()) {
326 notification_needed = false;
327 break;
328 }
329 #if defined(OS_ANDROID)
330 }
331 #else
332 }
333 #endif
334
335 // Don't track push messages that didn't show a notification but were exempt
336 // from needing to do so.
337 if (notification_shown || notification_needed) {
338 content::ServiceWorkerContext* service_worker_context =
339 content::BrowserContext::GetStoragePartitionForSite(
340 profile_, requesting_origin)->GetServiceWorkerContext();
341
342 PushMessagingService::GetNotificationsShownByLastFewPushes(
343 service_worker_context, service_worker_registration_id,
344 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown,
345 weak_factory_.GetWeakPtr(),
346 requesting_origin, service_worker_registration_id,
347 notification_shown, notification_needed));
348 } else {
349 RecordUserVisibleStatus(
350 content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_AND_NOT_SHOWN);
351 }
352 #endif // defined(ENABLE_NOTIFICATIONS)
353 }
354
355 static void IgnoreResult(bool unused) {
356 }
357
358 void PushMessagingServiceImpl::DidGetNotificationsShown(
359 const GURL& requesting_origin, int64 service_worker_registration_id,
360 bool notification_shown, bool notification_needed,
361 const std::string& data, bool success, bool not_found) {
362 content::ServiceWorkerContext* service_worker_context =
363 content::BrowserContext::GetStoragePartitionForSite(
364 profile_, requesting_origin)->GetServiceWorkerContext();
365
366 // We remember whether the last (up to) 10 pushes showed notifications.
367 const size_t MISSED_NOTIFICATIONS_LENGTH = 10;
368 // data is a string like "0001000", where '0' means shown, and '1' means
369 // needed but not shown. We manipulate it in bitset form.
370 std::bitset<MISSED_NOTIFICATIONS_LENGTH> missed_notifications(data);
371
372 bool needed_but_not_shown = notification_needed && !notification_shown;
373
374 // New entries go at the end, and old ones are shifted off the beginning once
375 // the history length is exceeded.
376 missed_notifications <<= 1;
377 missed_notifications[0] = needed_but_not_shown;
378 std::string updated_data(missed_notifications.
379 to_string<char, std::string::traits_type, std::string::allocator_type>());
380 PushMessagingService::SetNotificationsShownByLastFewPushes(
381 service_worker_context, service_worker_registration_id,
382 requesting_origin, updated_data,
383 base::Bind(&IgnoreResult)); // This is a heuristic; ignore failure.
384
385 if (notification_shown) {
386 RecordUserVisibleStatus(
387 notification_needed
388 ? content::PUSH_USER_VISIBLE_STATUS_REQUIRED_AND_SHOWN
389 : content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_BUT_SHOWN);
390 return;
391 }
392 if (needed_but_not_shown) {
393 if (missed_notifications.count() <= 1) {
394 RecordUserVisibleStatus(
395 content::PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_USED_GRACE);
396 return;
397 }
398 RecordUserVisibleStatus(
399 content::
400 PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED);
401 // The site failed to show a notification when one was needed, and they have
402 // already failed once in the previous 10 push messages, so we will show a
403 // generic notification. See https://crbug.com/437277.
404 // TODO(johnme): The generic notification should probably automatically
405 // close itself when the next push message arrives?
406 content::PlatformNotificationData notification_data;
407 // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698
408 notification_data.title = base::UTF8ToUTF16(requesting_origin.host());
409 notification_data.direction =
410 content::PlatformNotificationData::NotificationDirectionLeftToRight;
411 notification_data.body =
412 l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY);
413 notification_data.tag =
414 base::ASCIIToUTF16(kPushMessagingForcedNotificationTag);
415 notification_data.icon = GURL(); // TODO(johnme): Better icon?
416 PlatformNotificationServiceImpl* notification_service =
417 PlatformNotificationServiceImpl::GetInstance();
418 notification_service->DisplayPersistentNotification(
419 profile_,
420 service_worker_registration_id,
421 requesting_origin,
422 SkBitmap() /* icon */,
423 notification_data);
424 }
425 }
426
427 // Other GCMAppHandler methods -------------------------------------------------
428
429 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
430 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
431 // corresponding to app_id.
432 }
433
434 void PushMessagingServiceImpl::OnSendError(
435 const std::string& app_id,
436 const GCMClient::SendErrorDetails& send_error_details) {
437 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
438 }
439
440 void PushMessagingServiceImpl::OnSendAcknowledged(
441 const std::string& app_id,
442 const std::string& message_id) {
443 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
444 }
445
446 // GetPushEndpoint method ------------------------------------------------------
447
448 GURL PushMessagingServiceImpl::GetPushEndpoint() {
449 return GURL(std::string(kPushMessagingEndpoint));
450 }
451
452 // Register and GetPermissionStatus methods ------------------------------------
453
454 void PushMessagingServiceImpl::RegisterFromDocument(
455 const GURL& requesting_origin,
456 int64 service_worker_registration_id,
457 const std::string& sender_id,
458 int renderer_id,
459 int render_frame_id,
460 bool user_visible_only,
461 const content::PushMessagingService::RegisterCallback& callback) {
462 if (!gcm_profile_service_->driver()) {
463 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
464 return;
465 }
466
467 PushMessagingApplicationId application_id =
468 PushMessagingApplicationId::Generate(requesting_origin,
469 service_worker_registration_id);
470 DCHECK(application_id.IsValid());
471
472 if (push_registration_count_ + pending_push_registration_count_
473 >= kMaxRegistrations) {
474 RegisterEnd(callback,
475 std::string(),
476 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
477 return;
478 }
479
480 content::RenderFrameHost* render_frame_host =
481 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
482 if (!render_frame_host)
483 return;
484
485 content::WebContents* web_contents =
486 content::WebContents::FromRenderFrameHost(render_frame_host);
487 if (!web_contents)
488 return;
489
490 // TODO(miguelg) need to send this over IPC when bubble support is
491 // implemented.
492 int bridge_id = -1;
493
494 const PermissionRequestID id(
495 renderer_id, web_contents->GetRoutingID(), bridge_id, GURL());
496
497 gcm::PushMessagingPermissionContext* permission_context =
498 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
499
500 if (permission_context == NULL || !user_visible_only) {
501 RegisterEnd(callback,
502 std::string(),
503 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
504 return;
505 }
506
507 // TODO(miguelg): Consider the value of |user_visible_only| when making
508 // the permission request.
509 // TODO(mlamouri): Move requesting Push permission over to using Mojo, and
510 // re-introduce the ability of |user_gesture| when bubbles require this.
511 // https://crbug.com/423770.
512 permission_context->RequestPermission(
513 web_contents, id, requesting_origin, true /* user_gesture */,
514 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
515 weak_factory_.GetWeakPtr(), application_id, sender_id,
516 callback));
517 }
518
519 void PushMessagingServiceImpl::RegisterFromWorker(
520 const GURL& requesting_origin,
521 int64 service_worker_registration_id,
522 const std::string& sender_id,
523 const content::PushMessagingService::RegisterCallback& register_callback) {
524 if (!gcm_profile_service_->driver()) {
525 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
526 return;
527 }
528
529 PushMessagingApplicationId application_id =
530 PushMessagingApplicationId::Generate(requesting_origin,
531 service_worker_registration_id);
532 DCHECK(application_id.IsValid());
533
534 if (profile_->GetPrefs()->GetInteger(
535 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
536 RegisterEnd(register_callback, std::string(),
537 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
538 return;
539 }
540
541 GURL embedding_origin = requesting_origin;
542 blink::WebPushPermissionStatus permission_status =
543 PushMessagingServiceImpl::GetPermissionStatus(requesting_origin,
544 embedding_origin);
545 if (permission_status != blink::WebPushPermissionStatusGranted) {
546 RegisterEnd(register_callback, std::string(),
547 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
548 return;
549 }
550
551 IncreasePushRegistrationCount(1, true /* is_pending */);
552 std::vector<std::string> sender_ids(1, sender_id);
553 gcm_profile_service_->driver()->Register(
554 application_id.app_id_guid(), sender_ids,
555 base::Bind(&PushMessagingServiceImpl::DidRegister,
556 weak_factory_.GetWeakPtr(),
557 application_id, register_callback));
558 }
559
560 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
561 const GURL& requesting_origin,
562 const GURL& embedding_origin) {
563 PushMessagingPermissionContext* permission_context =
564 PushMessagingPermissionContextFactory::GetForProfile(profile_);
565 return ToPushPermission(permission_context->GetPermissionStatus(
566 requesting_origin, embedding_origin));
567 }
568
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);
574 }
575
576 void PushMessagingServiceImpl::DidRegister(
577 const PushMessagingApplicationId& application_id,
578 const content::PushMessagingService::RegisterCallback& callback,
579 const std::string& registration_id,
580 GCMClient::Result result) {
581 content::PushRegistrationStatus status =
582 content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
583 switch (result) {
584 case 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 GCMClient::INVALID_PARAMETER:
590 case GCMClient::GCM_DISABLED:
591 case GCMClient::ASYNC_OPERATION_PENDING:
592 case GCMClient::SERVER_ERROR:
593 case GCMClient::UNKNOWN_ERROR:
594 status = content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
595 break;
596 case GCMClient::NETWORK_ERROR:
597 case GCMClient::TTL_EXCEEDED:
598 status = content::PUSH_REGISTRATION_STATUS_NETWORK_ERROR;
599 break;
600 }
601 RegisterEnd(callback, registration_id, status);
602 DecreasePushRegistrationCount(1, true /* was_pending */);
603 }
604
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;
615 }
616
617 // The GCMDriver could be NULL if GCMProfileService has been shut down.
618 if (!gcm_profile_service_->driver())
619 return;
620
621 IncreasePushRegistrationCount(1, true /* is_pending */);
622 std::vector<std::string> sender_ids(1, sender_id);
623 gcm_profile_service_->driver()->Register(
624 application_id.app_id_guid(),
625 sender_ids,
626 base::Bind(&PushMessagingServiceImpl::DidRegister,
627 weak_factory_.GetWeakPtr(),
628 application_id, register_callback));
629 }
630
631 // Unregister methods ----------------------------------------------------------
632
633 void PushMessagingServiceImpl::Unregister(
634 const GURL& requesting_origin,
635 int64 service_worker_registration_id,
636 const std::string& sender_id,
637 bool retry_on_failure,
638 const content::PushMessagingService::UnregisterCallback& callback) {
639 DCHECK(gcm_profile_service_->driver());
640
641 PushMessagingApplicationId application_id = PushMessagingApplicationId::Get(
642 profile_, requesting_origin, service_worker_registration_id);
643 if (!application_id.IsValid()) {
644 if (!callback.is_null()) {
645 callback.Run(
646 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
647 }
648 return;
649 }
650
651 Unregister(application_id.app_id_guid(), sender_id, retry_on_failure,
652 callback);
653 }
654
655 void PushMessagingServiceImpl::Unregister(
656 const std::string& app_id_guid,
657 const std::string& sender_id,
658 bool retry_on_failure,
659 const content::PushMessagingService::UnregisterCallback& callback) {
660 DCHECK(gcm_profile_service_->driver());
661
662 if (retry_on_failure) {
663 // Delete the mapping for this app id, to guarantee that no messages get
664 // delivered in future (even if unregistration fails).
665 // TODO(johnme): Instead of deleting these app ids, store them elsewhere,
666 // and retry unregistration if it fails due to network errors.
667 PushMessagingApplicationId application_id =
668 PushMessagingApplicationId::Get(profile_, app_id_guid);
669 if (application_id.IsValid())
670 application_id.DeleteFromDisk(profile_);
671 }
672
673 const auto& unregister_callback =
674 base::Bind(&PushMessagingServiceImpl::DidUnregister,
675 weak_factory_.GetWeakPtr(),
676 app_id_guid, retry_on_failure, callback);
677 #if defined(OS_ANDROID)
678 // On Android the backend is different, and requires the original sender_id.
679 gcm_profile_service_->driver()->UnregisterWithSenderId(app_id_guid, sender_id,
680 unregister_callback);
681 #else
682 gcm_profile_service_->driver()->Unregister(app_id_guid, unregister_callback);
683 #endif
684 }
685
686 void PushMessagingServiceImpl::DidUnregister(
687 const std::string& app_id_guid,
688 bool retry_on_failure,
689 const content::PushMessagingService::UnregisterCallback& callback,
690 GCMClient::Result result) {
691 if (result == GCMClient::SUCCESS) {
692 PushMessagingApplicationId application_id =
693 PushMessagingApplicationId::Get(profile_, app_id_guid);
694 if (!application_id.IsValid()) {
695 if (!callback.is_null()) {
696 callback.Run(
697 content::PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
698 }
699 return;
700 }
701
702 application_id.DeleteFromDisk(profile_);
703 DecreasePushRegistrationCount(1, false /* was_pending */);
704 }
705
706 // Internal calls pass a null callback.
707 if (!callback.is_null()) {
708 switch (result) {
709 case GCMClient::SUCCESS:
710 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED);
711 break;
712 case GCMClient::INVALID_PARAMETER:
713 case GCMClient::GCM_DISABLED:
714 case GCMClient::ASYNC_OPERATION_PENDING:
715 case GCMClient::SERVER_ERROR:
716 case GCMClient::UNKNOWN_ERROR:
717 callback.Run(content::PUSH_UNREGISTRATION_STATUS_SERVICE_ERROR);
718 break;
719 case GCMClient::NETWORK_ERROR:
720 case GCMClient::TTL_EXCEEDED:
721 callback.Run(
722 retry_on_failure
723 ? content::
724 PUSH_UNREGISTRATION_STATUS_PENDING_WILL_RETRY_NETWORK_ERROR
725 : content::PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR);
726 break;
727 }
728 }
729 }
730
731 // OnContentSettingChanged methods ---------------------------------------------
732
733 void PushMessagingServiceImpl::OnContentSettingChanged(
734 const ContentSettingsPattern& primary_pattern,
735 const ContentSettingsPattern& secondary_pattern,
736 ContentSettingsType content_type,
737 std::string resource_identifier) {
738 if (content_type != CONTENT_SETTINGS_TYPE_PUSH_MESSAGING &&
739 content_type != CONTENT_SETTINGS_TYPE_NOTIFICATIONS) {
740 return;
741 }
742
743 for (const auto& id : PushMessagingApplicationId::GetAll(profile_)) {
744 // If |primary_pattern| is not valid, we should always check for a
745 // permission change because it can happen for example when the entire
746 // Push or Notifications permissions are cleared.
747 // Otherwise, the permission should be checked if the pattern matches the
748 // origin.
749 if (primary_pattern.IsValid() && !primary_pattern.Matches(id.origin()))
750 continue;
751
752 if (HasPermission(id.origin()))
753 continue;
754
755 PushMessagingService::GetSenderId(
756 profile_, id.origin(), id.service_worker_registration_id(),
757 base::Bind(
758 &PushMessagingServiceImpl::UnregisterBecausePermissionRevoked,
759 weak_factory_.GetWeakPtr(), id));
760 }
761 }
762
763 void PushMessagingServiceImpl::UnregisterBecausePermissionRevoked(
764 const PushMessagingApplicationId& id,
765 const std::string& sender_id, bool success, bool not_found) {
766 // Unregister the PushMessagingApplicationId with the push service.
767 Unregister(id.app_id_guid(), sender_id, true /* retry_on_failure */,
768 UnregisterCallback());
769
770 // Clear the associated service worker push registration id.
771 PushMessagingService::ClearPushRegistrationID(
772 profile_, id.origin(), id.service_worker_registration_id());
773 }
774
775 // Helper methods --------------------------------------------------------------
776
777 bool PushMessagingServiceImpl::HasPermission(const GURL& origin) {
778 gcm::PushMessagingPermissionContext* permission_context =
779 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
780 DCHECK(permission_context);
781
782 return permission_context->GetPermissionStatus(origin, origin) ==
783 CONTENT_SETTING_ALLOW;
784 }
785
786 void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
787 profile_ = profile;
788 profile_->GetHostContentSettingsMap()->AddObserver(this);
789 }
790
791 } // namespace gcm
OLDNEW
« no previous file with comments | « chrome/browser/services/gcm/push_messaging_service_impl.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698