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

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

Issue 883743002: Push API: Grace - allow one in ten pushes to show no notification. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@userdata
Patch Set: Use static methods on PushMessagingService to save state Created 5 years, 10 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
OLDNEW
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/services/gcm/push_messaging_service_impl.h" 5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h"
6 6
7 #include <vector> 7 #include <vector>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
(...skipping 11 matching lines...) Expand all
22 #include "chrome/browser/services/gcm/push_messaging_permission_context.h" 22 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
23 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory. h" 23 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory. h"
24 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/pref_names.h" 25 #include "chrome/common/pref_names.h"
26 #include "chrome/grit/generated_resources.h" 26 #include "chrome/grit/generated_resources.h"
27 #include "components/content_settings/core/common/permission_request_id.h" 27 #include "components/content_settings/core/common/permission_request_id.h"
28 #include "components/gcm_driver/gcm_driver.h" 28 #include "components/gcm_driver/gcm_driver.h"
29 #include "components/pref_registry/pref_registry_syncable.h" 29 #include "components/pref_registry/pref_registry_syncable.h"
30 #include "content/public/browser/browser_context.h" 30 #include "content/public/browser/browser_context.h"
31 #include "content/public/browser/render_frame_host.h" 31 #include "content/public/browser/render_frame_host.h"
32 #include "content/public/browser/service_worker_context.h"
33 #include "content/public/browser/storage_partition.h"
32 #include "content/public/browser/web_contents.h" 34 #include "content/public/browser/web_contents.h"
33 #include "content/public/common/child_process_host.h" 35 #include "content/public/common/child_process_host.h"
34 #include "content/public/common/content_switches.h" 36 #include "content/public/common/content_switches.h"
35 #include "content/public/common/platform_notification_data.h" 37 #include "content/public/common/platform_notification_data.h"
36 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 38 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
37 #include "third_party/skia/include/core/SkBitmap.h" 39 #include "third_party/skia/include/core/SkBitmap.h"
38 #include "ui/base/l10n/l10n_util.h" 40 #include "ui/base/l10n/l10n_util.h"
39 41
40 #if defined(OS_ANDROID) 42 #if defined(OS_ANDROID)
41 #include "chrome/browser/ui/android/tab_model/tab_model.h" 43 #include "chrome/browser/ui/android/tab_model/tab_model.h"
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 #if defined(ENABLE_NOTIFICATIONS) 245 #if defined(ENABLE_NOTIFICATIONS)
244 // TODO(johnme): Relax this heuristic slightly. 246 // TODO(johnme): Relax this heuristic slightly.
245 PlatformNotificationServiceImpl* notification_service = 247 PlatformNotificationServiceImpl* notification_service =
246 PlatformNotificationServiceImpl::GetInstance(); 248 PlatformNotificationServiceImpl::GetInstance();
247 // Can't use g_browser_process->notification_ui_manager(), since the test uses 249 // Can't use g_browser_process->notification_ui_manager(), since the test uses
248 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting. 250 // PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting.
249 // TODO(peter): Remove the need to use both APIs here once Notification.get() 251 // TODO(peter): Remove the need to use both APIs here once Notification.get()
250 // is supported. 252 // is supported.
251 int notification_count = notification_service->GetNotificationUIManager()-> 253 int notification_count = notification_service->GetNotificationUIManager()->
252 GetAllIdsByProfileAndSourceOrigin(profile_, application_id.origin).size(); 254 GetAllIdsByProfileAndSourceOrigin(profile_, application_id.origin).size();
253 if (notification_count > 0) 255 // TODO(johnme): Hiding an existing notification should also count as a useful
254 return; 256 // user-visible action done in response to a push message - but make sure that
257 // sending two messages in rapid succession which show then hide a
258 // notification doesn't count.
259 bool notification_shown = notification_count > 0;
255 260
256 // Sites with a currently visible tab don't need to show notifications. 261 bool notification_needed = true;
262 if (!notification_shown) {
263 // Sites with a currently visible tab don't need to show notifications.
257 #if defined(OS_ANDROID) 264 #if defined(OS_ANDROID)
258 for (TabModel* tab_model : TabModelList) { 265 for (TabModel* tab_model : TabModelList) {
259 Profile* profile = tab_model->GetProfile(); 266 Profile* profile = tab_model->GetProfile();
260 content::WebContents* active_web_contents = 267 content::WebContents* active_web_contents =
261 tab_model->GetActiveWebContents(); 268 tab_model->GetActiveWebContents();
262 #else 269 #else
263 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 270 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
264 Profile* profile = it->profile(); 271 Profile* profile = it->profile();
265 content::WebContents* active_web_contents = 272 content::WebContents* active_web_contents =
266 it->tab_strip_model()->GetActiveWebContents(); 273 it->tab_strip_model()->GetActiveWebContents();
267 #endif 274 #endif
268 if (!active_web_contents) 275 if (!active_web_contents)
269 continue; 276 continue;
270 277
271 // Don't leak information from other profiles. 278 // Don't leak information from other profiles.
272 if (profile != profile_) 279 if (profile != profile_)
273 continue; 280 continue;
274 281
275 // Ignore minimized windows etc. 282 // Ignore minimized windows etc.
276 switch (active_web_contents->GetMainFrame()->GetVisibilityState()) { 283 switch (active_web_contents->GetMainFrame()->GetVisibilityState()) {
277 case blink::WebPageVisibilityStateHidden: 284 case blink::WebPageVisibilityStateHidden:
278 case blink::WebPageVisibilityStatePrerender: 285 case blink::WebPageVisibilityStatePrerender:
279 continue; 286 continue;
280 case blink::WebPageVisibilityStateVisible: 287 case blink::WebPageVisibilityStateVisible:
281 break; 288 break;
282 } 289 }
283 290
284 // Use the visible URL since that's the one the user is aware of (and it 291 // Use the visible URL since that's the one the user is aware of (and it
285 // doesn't matter whether the page loaded successfully). 292 // doesn't matter whether the page loaded successfully).
286 const GURL& active_url = active_web_contents->GetVisibleURL(); 293 const GURL& active_url = active_web_contents->GetVisibleURL();
287 294
288 // Allow https://foo.example.com Service Worker to not show notification if 295 // Allow https://foo.example.com Service Worker to not show notification i f
289 // an https://bar.example.com tab is visible (and hence might conceivably 296 // an https://bar.example.com tab is visible (and hence might conceivably
290 // be showing UI in response to the push message); but http:// doesn't count 297 // be showing UI in response to the push message); but http:// doesn't cou nt
291 // as the Service Worker can't talk to it, even with navigator.connect. 298 // as the Service Worker can't talk to it, even with navigator.connect.
Avi (use Gerrit) 2015/02/04 16:16:57 rewrap the comment to stay under 80 columns
johnme 2015/02/04 17:57:41 Done.
292 if (application_id.origin.scheme() != active_url.scheme()) 299 if (application_id.origin.scheme() != active_url.scheme())
293 continue; 300 continue;
294 if (net::registry_controlled_domains::SameDomainOrHost( 301 if (net::registry_controlled_domains::SameDomainOrHost(
295 application_id.origin, active_url, 302 application_id.origin, active_url,
296 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { 303 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
297 return; 304 notification_needed = false;
305 break;
306 }
298 } 307 }
299 } 308 }
300 309
301 // If we haven't returned yet, the site failed to show a notification, so we 310 content::ServiceWorkerContext* service_worker_context =
302 // will show a generic notification. See https://crbug.com/437277 311 content::BrowserContext::GetStoragePartitionForSite(
303 // TODO(johnme): The generic notification should probably automatically close 312 profile_, application_id.origin)->GetServiceWorkerContext();
304 // itself when the next push message arrives? 313
305 content::PlatformNotificationData notification_data; 314 PushMessagingService::GetNotificationsShownByLastFewPushes(
306 // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698 315 service_worker_context, application_id.service_worker_registration_id,
307 notification_data.title = l10n_util::GetStringFUTF16( 316 base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown,
308 IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_TITLE, 317 weak_factory_.GetWeakPtr(),
309 base::UTF8ToUTF16(application_id.origin.host())); 318 application_id, notification_shown, notification_needed));
310 notification_data.direction = 319 #endif // defined(ENABLE_NOTIFICATIONS)
311 content::PlatformNotificationData::NotificationDirectionLeftToRight; 320 }
312 notification_data.body = 321
313 l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY); 322 static void IgnoreResult(bool unused) {
314 notification_data.tag = 323 }
315 base::ASCIIToUTF16(kPushMessagingForcedNotificationTag); 324
316 notification_data.icon = GURL(); // TODO(johnme): Better icon? 325 void PushMessagingServiceImpl::DidGetNotificationsShown(
317 notification_service->DisplayPersistentNotification( 326 const PushMessagingApplicationId& application_id,
318 profile_, 327 bool notification_shown, bool notification_needed,
319 application_id.service_worker_registration_id, 328 const std::string& data, bool success, bool not_found) {
320 application_id.origin, 329 // We remember whether the last (up to) 10 pushes showed notifications.
321 SkBitmap() /* icon */, 330 const size_t NOTIFICATION_HISTORY_LENGTH = 10;
322 notification_data, 331 // data is a string like "1110111", where '1' means shown, and '0' means
323 content::ChildProcessHost::kInvalidUniqueID /* render_process_id */); 332 // needed but not shown. New entries go at the end, and old ones are shifted
324 #endif 333 // off the beginning once the history length is exceeded.
Avi (use Gerrit) 2015/02/04 16:16:57 :( I'd much rather have a type like std::bitset o
johnme 2015/02/04 17:57:41 Done (I switched to std::bitset since it can easil
334
335 if (notification_needed && !notification_shown
336 && data.find('0') != std::string::npos) {
337 // The site failed to show a notification when one was needed, and they have
338 // already failed once in the previous 10 push messages, so we will show a
339 // generic notification. See https://crbug.com/437277.
340 // TODO(johnme): The generic notification should probably automatically
341 // close itself when the next push message arrives?
342 content::PlatformNotificationData notification_data;
343 // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698
344 notification_data.title = l10n_util::GetStringFUTF16(
345 IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_TITLE,
346 base::UTF8ToUTF16(application_id.origin.host()));
347 notification_data.direction =
348 content::PlatformNotificationData::NotificationDirectionLeftToRight;
349 notification_data.body =
350 l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY);
351 notification_data.tag =
352 base::ASCIIToUTF16(kPushMessagingForcedNotificationTag);
353 notification_data.icon = GURL(); // TODO(johnme): Better icon?
354 PlatformNotificationServiceImpl* notification_service =
355 PlatformNotificationServiceImpl::GetInstance();
356 notification_service->DisplayPersistentNotification(
357 profile_,
358 application_id.service_worker_registration_id,
359 application_id.origin,
360 SkBitmap() /* icon */,
361 notification_data,
362 content::ChildProcessHost::kInvalidUniqueID /* render_process_id */);
363 }
364
365 // Don't track push messages that didn't show a notification but were exempt
366 // from needing to do so.
367 if (notification_shown || notification_needed) {
368 char new_entry = notification_shown ? '1' : '0';
Avi (use Gerrit) 2015/02/04 16:16:57 :( // see above
johnme 2015/02/04 17:57:41 Done.
369 std::string updatedData = data.size() >= NOTIFICATION_HISTORY_LENGTH
370 ? data.substr(1) + new_entry
371 : data + new_entry;
372
373 content::ServiceWorkerContext* service_worker_context =
374 content::BrowserContext::GetStoragePartitionForSite(
375 profile_, application_id.origin)->GetServiceWorkerContext();
376
377 PushMessagingService::SetNotificationsShownByLastFewPushes(
378 service_worker_context, application_id.service_worker_registration_id,
379 application_id.origin, updatedData,
380 base::Bind(&IgnoreResult)); // This is a heuristic; ignore failure.
381 }
325 } 382 }
326 383
327 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { 384 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
328 // TODO(mvanouwerkerk): Fire push error event on the Service Worker 385 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
329 // corresponding to app_id. 386 // corresponding to app_id.
330 } 387 }
331 388
332 void PushMessagingServiceImpl::OnSendError( 389 void PushMessagingServiceImpl::OnSendError(
333 const std::string& app_id, 390 const std::string& app_id,
334 const GCMClient::SendErrorDetails& send_error_details) { 391 const GCMClient::SendErrorDetails& send_error_details) {
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
559 bool PushMessagingServiceImpl::HasPermission(const GURL& origin) { 616 bool PushMessagingServiceImpl::HasPermission(const GURL& origin) {
560 gcm::PushMessagingPermissionContext* permission_context = 617 gcm::PushMessagingPermissionContext* permission_context =
561 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_); 618 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
562 DCHECK(permission_context); 619 DCHECK(permission_context);
563 620
564 return permission_context->GetPermissionStatus(origin, origin) == 621 return permission_context->GetPermissionStatus(origin, origin) ==
565 CONTENT_SETTING_ALLOW; 622 CONTENT_SETTING_ALLOW;
566 } 623 }
567 624
568 } // namespace gcm 625 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698