OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/push_messaging/push_messaging_service_impl.h" | 5 #include "chrome/browser/push_messaging/push_messaging_service_impl.h" |
6 | 6 |
7 #include <bitset> | 7 #include <bitset> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/barrier_closure.h" | 10 #include "base/barrier_closure.h" |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 // Shutdown() should come before and it removes us from the list of app | 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. | 178 // handlers of gcm::GCMDriver so this shouldn't ever been called. |
179 NOTREACHED(); | 179 NOTREACHED(); |
180 } | 180 } |
181 | 181 |
182 // OnMessage methods ----------------------------------------------------------- | 182 // OnMessage methods ----------------------------------------------------------- |
183 | 183 |
184 void PushMessagingServiceImpl::OnMessage( | 184 void PushMessagingServiceImpl::OnMessage( |
185 const std::string& app_id, | 185 const std::string& app_id, |
186 const gcm::GCMClient::IncomingMessage& message) { | 186 const gcm::GCMClient::IncomingMessage& message) { |
| 187 base::Closure message_handled_closure = |
| 188 message_callback_for_testing_.is_null() ? base::Bind(&base::DoNothing) |
| 189 : message_callback_for_testing_; |
187 PushMessagingApplicationId application_id = | 190 PushMessagingApplicationId application_id = |
188 PushMessagingApplicationId::Get(profile_, app_id); | 191 PushMessagingApplicationId::Get(profile_, app_id); |
189 // Drop message and unregister if app id was unknown (maybe recently deleted). | 192 // Drop message and unregister if app id was unknown (maybe recently deleted). |
190 if (!application_id.IsValid()) { | 193 if (!application_id.IsValid()) { |
191 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message, | 194 DeliverMessageCallback(app_id, GURL::EmptyGURL(), -1, message, |
| 195 message_handled_closure, |
192 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID); | 196 content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID); |
193 return; | 197 return; |
194 } | 198 } |
195 // Drop message and unregister if |origin| has lost push permission. | 199 // Drop message and unregister if |origin| has lost push permission. |
196 if (!HasPermission(application_id.origin())) { | 200 if (!HasPermission(application_id.origin())) { |
197 DeliverMessageCallback(app_id, application_id.origin(), | 201 DeliverMessageCallback(app_id, application_id.origin(), |
198 application_id.service_worker_registration_id(), | 202 application_id.service_worker_registration_id(), |
199 message, | 203 message, message_handled_closure, |
200 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED); | 204 content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED); |
201 return; | 205 return; |
202 } | 206 } |
203 | 207 |
204 rappor::SampleDomainAndRegistryFromGURL( | 208 rappor::SampleDomainAndRegistryFromGURL( |
205 g_browser_process->rappor_service(), | 209 g_browser_process->rappor_service(), |
206 "PushMessaging.MessageReceived.Origin", | 210 "PushMessaging.MessageReceived.Origin", |
207 application_id.origin()); | 211 application_id.origin()); |
208 | 212 |
209 // The Push API only exposes a single string of data in the push event fired | 213 // The Push API only exposes a single string of data in the push event fired |
(...skipping 21 matching lines...) Expand all Loading... |
231 } | 235 } |
232 | 236 |
233 content::BrowserContext::DeliverPushMessage( | 237 content::BrowserContext::DeliverPushMessage( |
234 profile_, | 238 profile_, |
235 application_id.origin(), | 239 application_id.origin(), |
236 application_id.service_worker_registration_id(), | 240 application_id.service_worker_registration_id(), |
237 data, | 241 data, |
238 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback, | 242 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback, |
239 weak_factory_.GetWeakPtr(), | 243 weak_factory_.GetWeakPtr(), |
240 application_id.app_id_guid(), application_id.origin(), | 244 application_id.app_id_guid(), application_id.origin(), |
241 application_id.service_worker_registration_id(), message)); | 245 application_id.service_worker_registration_id(), message, |
| 246 message_handled_closure)); |
242 } | 247 } |
243 | 248 |
244 void PushMessagingServiceImpl::DeliverMessageCallback( | 249 void PushMessagingServiceImpl::DeliverMessageCallback( |
245 const std::string& app_id_guid, | 250 const std::string& app_id_guid, |
246 const GURL& requesting_origin, | 251 const GURL& requesting_origin, |
247 int64 service_worker_registration_id, | 252 int64 service_worker_registration_id, |
248 const gcm::GCMClient::IncomingMessage& message, | 253 const gcm::GCMClient::IncomingMessage& message, |
| 254 const base::Closure& message_handled_closure, |
249 content::PushDeliveryStatus status) { | 255 content::PushDeliveryStatus status) { |
250 // TODO(mvanouwerkerk): Show a warning in the developer console of the | 256 // TODO(mvanouwerkerk): Show a warning in the developer console of the |
251 // Service Worker corresponding to app_id (and/or on an internals page). | 257 // Service Worker corresponding to app_id (and/or on an internals page). |
252 // TODO(mvanouwerkerk): Is there a way to recover from failure? | 258 // TODO(mvanouwerkerk): Is there a way to recover from failure? |
253 switch (status) { | 259 switch (status) { |
254 // Call RequireUserVisibleUX if the message was delivered to the Service | 260 // Call RequireUserVisibleUX if the message was delivered to the Service |
255 // Worker JS, even if the website's event handler failed (to prevent sites | 261 // Worker JS, even if the website's event handler failed (to prevent sites |
256 // deliberately failing in order to avoid having to show notifications). | 262 // deliberately failing in order to avoid having to show notifications). |
257 case content::PUSH_DELIVERY_STATUS_SUCCESS: | 263 case content::PUSH_DELIVERY_STATUS_SUCCESS: |
258 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED: | 264 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED: |
259 RequireUserVisibleUX(requesting_origin, service_worker_registration_id); | 265 RequireUserVisibleUX(requesting_origin, service_worker_registration_id, |
| 266 message_handled_closure); |
260 break; | 267 break; |
261 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE: | 268 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE: |
262 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR: | 269 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR: |
| 270 message_handled_closure.Run(); |
263 break; | 271 break; |
264 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID: | 272 case content::PUSH_DELIVERY_STATUS_UNKNOWN_APP_ID: |
265 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED: | 273 case content::PUSH_DELIVERY_STATUS_PERMISSION_DENIED: |
266 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER: | 274 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER: |
267 Unregister(app_id_guid, message.sender_id, UnregisterCallback()); | 275 Unregister(app_id_guid, message.sender_id, |
| 276 base::Bind(&UnregisterCallbackToClosure, |
| 277 message_handled_closure)); |
268 break; | 278 break; |
269 } | 279 } |
270 RecordDeliveryStatus(status); | 280 RecordDeliveryStatus(status); |
271 } | 281 } |
272 | 282 |
273 void PushMessagingServiceImpl::RequireUserVisibleUX( | 283 void PushMessagingServiceImpl::RequireUserVisibleUX( |
274 const GURL& requesting_origin, int64 service_worker_registration_id) { | 284 const GURL& requesting_origin, int64 service_worker_registration_id, |
| 285 const base::Closure& message_handled_closure) { |
275 #if defined(ENABLE_NOTIFICATIONS) | 286 #if defined(ENABLE_NOTIFICATIONS) |
276 // TODO(johnme): Relax this heuristic slightly. | 287 // TODO(johnme): Relax this heuristic slightly. |
277 PlatformNotificationServiceImpl* notification_service = | 288 PlatformNotificationServiceImpl* notification_service = |
278 PlatformNotificationServiceImpl::GetInstance(); | 289 PlatformNotificationServiceImpl::GetInstance(); |
279 // Can't use g_browser_process->notification_ui_manager(), since the test uses | 290 // Can't use g_browser_process->notification_ui_manager(), since the test uses |
280 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting. | 291 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting. |
281 // TODO(peter): Remove the need to use both APIs here once Notification.get() | 292 // TODO(peter): Remove the need to use both APIs here once Notification.get() |
282 // is supported. | 293 // is supported. |
283 int notification_count = notification_service->GetNotificationUIManager()-> | 294 int notification_count = notification_service->GetNotificationUIManager()-> |
284 GetAllIdsByProfileAndSourceOrigin(profile_, requesting_origin).size(); | 295 GetAllIdsByProfileAndSourceOrigin(profile_, requesting_origin).size(); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 if (notification_shown || notification_needed) { | 346 if (notification_shown || notification_needed) { |
336 content::ServiceWorkerContext* service_worker_context = | 347 content::ServiceWorkerContext* service_worker_context = |
337 content::BrowserContext::GetStoragePartitionForSite( | 348 content::BrowserContext::GetStoragePartitionForSite( |
338 profile_, requesting_origin)->GetServiceWorkerContext(); | 349 profile_, requesting_origin)->GetServiceWorkerContext(); |
339 | 350 |
340 GetNotificationsShownByLastFewPushes( | 351 GetNotificationsShownByLastFewPushes( |
341 service_worker_context, service_worker_registration_id, | 352 service_worker_context, service_worker_registration_id, |
342 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown, | 353 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown, |
343 weak_factory_.GetWeakPtr(), | 354 weak_factory_.GetWeakPtr(), |
344 requesting_origin, service_worker_registration_id, | 355 requesting_origin, service_worker_registration_id, |
345 notification_shown, notification_needed)); | 356 notification_shown, notification_needed, |
| 357 message_handled_closure)); |
346 } else { | 358 } else { |
347 RecordUserVisibleStatus( | 359 RecordUserVisibleStatus( |
348 content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_AND_NOT_SHOWN); | 360 content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_AND_NOT_SHOWN); |
| 361 message_handled_closure.Run(); |
349 } | 362 } |
| 363 #else |
| 364 message_handled_closure.Run(); |
350 #endif // defined(ENABLE_NOTIFICATIONS) | 365 #endif // defined(ENABLE_NOTIFICATIONS) |
351 } | 366 } |
352 | 367 |
353 static void IgnoreResult(bool unused) { | 368 static void IgnoreResult(bool unused) { |
354 } | 369 } |
355 | 370 |
356 void PushMessagingServiceImpl::DidGetNotificationsShown( | 371 void PushMessagingServiceImpl::DidGetNotificationsShown( |
357 const GURL& requesting_origin, int64 service_worker_registration_id, | 372 const GURL& requesting_origin, int64 service_worker_registration_id, |
358 bool notification_shown, bool notification_needed, | 373 bool notification_shown, bool notification_needed, |
| 374 const base::Closure& message_handled_closure, |
359 const std::string& data, bool success, bool not_found) { | 375 const std::string& data, bool success, bool not_found) { |
360 content::ServiceWorkerContext* service_worker_context = | 376 content::ServiceWorkerContext* service_worker_context = |
361 content::BrowserContext::GetStoragePartitionForSite( | 377 content::BrowserContext::GetStoragePartitionForSite( |
362 profile_, requesting_origin)->GetServiceWorkerContext(); | 378 profile_, requesting_origin)->GetServiceWorkerContext(); |
363 | 379 |
364 // We remember whether the last (up to) 10 pushes showed notifications. | 380 // We remember whether the last (up to) 10 pushes showed notifications. |
365 const size_t MISSED_NOTIFICATIONS_LENGTH = 10; | 381 const size_t MISSED_NOTIFICATIONS_LENGTH = 10; |
366 // data is a string like "0001000", where '0' means shown, and '1' means | 382 // data is a string like "0001000", where '0' means shown, and '1' means |
367 // needed but not shown. We manipulate it in bitset form. | 383 // needed but not shown. We manipulate it in bitset form. |
368 std::bitset<MISSED_NOTIFICATIONS_LENGTH> missed_notifications(data); | 384 std::bitset<MISSED_NOTIFICATIONS_LENGTH> missed_notifications(data); |
369 | 385 |
370 bool needed_but_not_shown = notification_needed && !notification_shown; | 386 bool needed_but_not_shown = notification_needed && !notification_shown; |
371 | 387 |
372 // New entries go at the end, and old ones are shifted off the beginning once | 388 // New entries go at the end, and old ones are shifted off the beginning once |
373 // the history length is exceeded. | 389 // the history length is exceeded. |
374 missed_notifications <<= 1; | 390 missed_notifications <<= 1; |
375 missed_notifications[0] = needed_but_not_shown; | 391 missed_notifications[0] = needed_but_not_shown; |
376 std::string updated_data(missed_notifications. | 392 std::string updated_data(missed_notifications. |
377 to_string<char, std::string::traits_type, std::string::allocator_type>()); | 393 to_string<char, std::string::traits_type, std::string::allocator_type>()); |
378 SetNotificationsShownByLastFewPushes( | 394 SetNotificationsShownByLastFewPushes( |
379 service_worker_context, service_worker_registration_id, | 395 service_worker_context, service_worker_registration_id, |
380 requesting_origin, updated_data, | 396 requesting_origin, updated_data, |
381 base::Bind(&IgnoreResult)); // This is a heuristic; ignore failure. | 397 base::Bind(&IgnoreResult)); // This is a heuristic; ignore failure. |
382 | 398 |
383 if (notification_shown) { | 399 if (notification_shown) { |
384 RecordUserVisibleStatus( | 400 RecordUserVisibleStatus( |
385 notification_needed | 401 notification_needed |
386 ? content::PUSH_USER_VISIBLE_STATUS_REQUIRED_AND_SHOWN | 402 ? content::PUSH_USER_VISIBLE_STATUS_REQUIRED_AND_SHOWN |
387 : content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_BUT_SHOWN); | 403 : content::PUSH_USER_VISIBLE_STATUS_NOT_REQUIRED_BUT_SHOWN); |
| 404 message_handled_closure.Run(); |
388 return; | 405 return; |
389 } | 406 } |
390 if (needed_but_not_shown) { | 407 if (needed_but_not_shown) { |
391 if (missed_notifications.count() <= 1) { // apply grace | 408 if (missed_notifications.count() <= 1) { // apply grace |
392 RecordUserVisibleStatus( | 409 RecordUserVisibleStatus( |
393 content::PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_USED_GRACE); | 410 content::PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_USED_GRACE); |
| 411 message_handled_closure.Run(); |
394 return; | 412 return; |
395 } | 413 } |
396 RecordUserVisibleStatus( | 414 RecordUserVisibleStatus( |
397 content:: | 415 content:: |
398 PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED); | 416 PUSH_USER_VISIBLE_STATUS_REQUIRED_BUT_NOT_SHOWN_GRACE_EXCEEDED); |
399 rappor::SampleDomainAndRegistryFromGURL( | 417 rappor::SampleDomainAndRegistryFromGURL( |
400 g_browser_process->rappor_service(), | 418 g_browser_process->rappor_service(), |
401 "PushMessaging.GenericNotificationShown.Origin", | 419 "PushMessaging.GenericNotificationShown.Origin", |
402 requesting_origin); | 420 requesting_origin); |
403 // The site failed to show a notification when one was needed, and they have | 421 // The site failed to show a notification when one was needed, and they have |
(...skipping 12 matching lines...) Expand all Loading... |
416 notification_data.icon = GURL(); // TODO(johnme): Better icon? | 434 notification_data.icon = GURL(); // TODO(johnme): Better icon? |
417 notification_data.silent = true; | 435 notification_data.silent = true; |
418 PlatformNotificationServiceImpl* notification_service = | 436 PlatformNotificationServiceImpl* notification_service = |
419 PlatformNotificationServiceImpl::GetInstance(); | 437 PlatformNotificationServiceImpl::GetInstance(); |
420 notification_service->DisplayPersistentNotification( | 438 notification_service->DisplayPersistentNotification( |
421 profile_, | 439 profile_, |
422 service_worker_registration_id, | 440 service_worker_registration_id, |
423 requesting_origin, | 441 requesting_origin, |
424 SkBitmap() /* icon */, | 442 SkBitmap() /* icon */, |
425 notification_data); | 443 notification_data); |
| 444 message_handled_closure.Run(); |
426 } | 445 } |
427 } | 446 } |
428 | 447 |
| 448 void PushMessagingServiceImpl::SetMessageCallbackForTesting( |
| 449 const base::Closure& callback) { |
| 450 message_callback_for_testing_ = callback; |
| 451 } |
| 452 |
429 // Other gcm::GCMAppHandler methods ------------------------------------------- | 453 // Other gcm::GCMAppHandler methods ------------------------------------------- |
430 | 454 |
431 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { | 455 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { |
432 // TODO(mvanouwerkerk): Fire push error event on the Service Worker | 456 // TODO(mvanouwerkerk): Fire push error event on the Service Worker |
433 // corresponding to app_id. | 457 // corresponding to app_id. |
434 } | 458 } |
435 | 459 |
436 void PushMessagingServiceImpl::OnSendError( | 460 void PushMessagingServiceImpl::OnSendError( |
437 const std::string& app_id, | 461 const std::string& app_id, |
438 const gcm::GCMClient::SendErrorDetails& send_error_details) { | 462 const gcm::GCMClient::SendErrorDetails& send_error_details) { |
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
796 CONTENT_SETTING_ALLOW; | 820 CONTENT_SETTING_ALLOW; |
797 } | 821 } |
798 | 822 |
799 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const { | 823 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const { |
800 gcm::GCMProfileService* gcm_profile_service = | 824 gcm::GCMProfileService* gcm_profile_service = |
801 gcm::GCMProfileServiceFactory::GetForProfile(profile_); | 825 gcm::GCMProfileServiceFactory::GetForProfile(profile_); |
802 CHECK(gcm_profile_service); | 826 CHECK(gcm_profile_service); |
803 CHECK(gcm_profile_service->driver()); | 827 CHECK(gcm_profile_service->driver()); |
804 return gcm_profile_service->driver(); | 828 return gcm_profile_service->driver(); |
805 } | 829 } |
OLD | NEW |