Chromium Code Reviews| Index: chrome/browser/services/gcm/push_messaging_service_impl.cc |
| diff --git a/chrome/browser/services/gcm/push_messaging_service_impl.cc b/chrome/browser/services/gcm/push_messaging_service_impl.cc |
| index 1c314c4409a6d4b23a76b970a6392a8f12fdb742..493de5c36f8b043489f68222c42f229b5f862d3f 100644 |
| --- a/chrome/browser/services/gcm/push_messaging_service_impl.cc |
| +++ b/chrome/browser/services/gcm/push_messaging_service_impl.cc |
| @@ -29,6 +29,8 @@ |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/render_frame_host.h" |
| +#include "content/public/browser/service_worker_context.h" |
| +#include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/child_process_host.h" |
| #include "content/public/common/content_switches.h" |
| @@ -250,78 +252,133 @@ void PushMessagingServiceImpl::RequireUserVisibleUX( |
| // is supported. |
| int notification_count = notification_service->GetNotificationUIManager()-> |
| GetAllIdsByProfileAndSourceOrigin(profile_, application_id.origin).size(); |
| - if (notification_count > 0) |
| - return; |
| - |
| - // Sites with a currently visible tab don't need to show notifications. |
| + // TODO(johnme): Hiding an existing notification should also count as a useful |
| + // user-visible action done in response to a push message - but make sure that |
| + // sending two messages in rapid succession which show then hide a |
| + // notification doesn't count. |
| + bool notification_shown = notification_count > 0; |
| + |
| + bool notification_needed = true; |
| + if (!notification_shown) { |
| + // Sites with a currently visible tab don't need to show notifications. |
| #if defined(OS_ANDROID) |
| - for (TabModel* tab_model : TabModelList) { |
| - Profile* profile = tab_model->GetProfile(); |
| - content::WebContents* active_web_contents = |
| - tab_model->GetActiveWebContents(); |
| + for (TabModel* tab_model : TabModelList) { |
| + Profile* profile = tab_model->GetProfile(); |
| + content::WebContents* active_web_contents = |
| + tab_model->GetActiveWebContents(); |
| #else |
| - for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| - Profile* profile = it->profile(); |
| - content::WebContents* active_web_contents = |
| - it->tab_strip_model()->GetActiveWebContents(); |
| + for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| + Profile* profile = it->profile(); |
| + content::WebContents* active_web_contents = |
| + it->tab_strip_model()->GetActiveWebContents(); |
| #endif |
| - if (!active_web_contents) |
| - continue; |
| - |
| - // Don't leak information from other profiles. |
| - if (profile != profile_) |
| - continue; |
| - |
| - // Ignore minimized windows etc. |
| - switch (active_web_contents->GetMainFrame()->GetVisibilityState()) { |
| - case blink::WebPageVisibilityStateHidden: |
| - case blink::WebPageVisibilityStatePrerender: |
| - continue; |
| - case blink::WebPageVisibilityStateVisible: |
| - break; |
| + if (!active_web_contents) |
| + continue; |
| + |
| + // Don't leak information from other profiles. |
| + if (profile != profile_) |
| + continue; |
| + |
| + // Ignore minimized windows etc. |
| + switch (active_web_contents->GetMainFrame()->GetVisibilityState()) { |
| + case blink::WebPageVisibilityStateHidden: |
| + case blink::WebPageVisibilityStatePrerender: |
| + continue; |
| + case blink::WebPageVisibilityStateVisible: |
| + break; |
| + } |
| + |
| + // Use the visible URL since that's the one the user is aware of (and it |
| + // doesn't matter whether the page loaded successfully). |
| + const GURL& active_url = active_web_contents->GetVisibleURL(); |
| + |
| + // Allow https://foo.example.com Service Worker to not show notification if |
| + // an https://bar.example.com tab is visible (and hence might conceivably |
| + // be showing UI in response to the push message); but http:// doesn't count |
| + // 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.
|
| + if (application_id.origin.scheme() != active_url.scheme()) |
| + continue; |
| + if (net::registry_controlled_domains::SameDomainOrHost( |
| + application_id.origin, active_url, |
| + net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { |
| + notification_needed = false; |
| + break; |
| + } |
| } |
| + } |
| - // Use the visible URL since that's the one the user is aware of (and it |
| - // doesn't matter whether the page loaded successfully). |
| - const GURL& active_url = active_web_contents->GetVisibleURL(); |
| - |
| - // Allow https://foo.example.com Service Worker to not show notification if |
| - // an https://bar.example.com tab is visible (and hence might conceivably |
| - // be showing UI in response to the push message); but http:// doesn't count |
| - // as the Service Worker can't talk to it, even with navigator.connect. |
| - if (application_id.origin.scheme() != active_url.scheme()) |
| - continue; |
| - if (net::registry_controlled_domains::SameDomainOrHost( |
| - application_id.origin, active_url, |
| - net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) { |
| - return; |
| - } |
| + content::ServiceWorkerContext* service_worker_context = |
| + content::BrowserContext::GetStoragePartitionForSite( |
| + profile_, application_id.origin)->GetServiceWorkerContext(); |
| + |
| + PushMessagingService::GetNotificationsShownByLastFewPushes( |
| + service_worker_context, application_id.service_worker_registration_id, |
| + base::Bind(&PushMessagingServiceImpl::DidGetNotificationsShown, |
| + weak_factory_.GetWeakPtr(), |
| + application_id, notification_shown, notification_needed)); |
| +#endif // defined(ENABLE_NOTIFICATIONS) |
| +} |
| + |
| +static void IgnoreResult(bool unused) { |
| +} |
| + |
| +void PushMessagingServiceImpl::DidGetNotificationsShown( |
| + const PushMessagingApplicationId& application_id, |
| + bool notification_shown, bool notification_needed, |
| + const std::string& data, bool success, bool not_found) { |
| + // We remember whether the last (up to) 10 pushes showed notifications. |
| + const size_t NOTIFICATION_HISTORY_LENGTH = 10; |
| + // data is a string like "1110111", where '1' means shown, and '0' means |
| + // needed but not shown. New entries go at the end, and old ones are shifted |
| + // 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
|
| + |
| + if (notification_needed && !notification_shown |
| + && data.find('0') != std::string::npos) { |
| + // The site failed to show a notification when one was needed, and they have |
| + // already failed once in the previous 10 push messages, so we will show a |
| + // generic notification. See https://crbug.com/437277. |
| + // TODO(johnme): The generic notification should probably automatically |
| + // close itself when the next push message arrives? |
| + content::PlatformNotificationData notification_data; |
| + // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698 |
| + notification_data.title = l10n_util::GetStringFUTF16( |
| + IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_TITLE, |
| + base::UTF8ToUTF16(application_id.origin.host())); |
| + notification_data.direction = |
| + content::PlatformNotificationData::NotificationDirectionLeftToRight; |
| + notification_data.body = |
| + l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY); |
| + notification_data.tag = |
| + base::ASCIIToUTF16(kPushMessagingForcedNotificationTag); |
| + notification_data.icon = GURL(); // TODO(johnme): Better icon? |
| + PlatformNotificationServiceImpl* notification_service = |
| + PlatformNotificationServiceImpl::GetInstance(); |
| + notification_service->DisplayPersistentNotification( |
| + profile_, |
| + application_id.service_worker_registration_id, |
| + application_id.origin, |
| + SkBitmap() /* icon */, |
| + notification_data, |
| + content::ChildProcessHost::kInvalidUniqueID /* render_process_id */); |
| } |
| - // If we haven't returned yet, the site failed to show a notification, so we |
| - // will show a generic notification. See https://crbug.com/437277 |
| - // TODO(johnme): The generic notification should probably automatically close |
| - // itself when the next push message arrives? |
| - content::PlatformNotificationData notification_data; |
| - // TODO(johnme): Switch to FormatOriginForDisplay from crbug.com/402698 |
| - notification_data.title = l10n_util::GetStringFUTF16( |
| - IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_TITLE, |
| - base::UTF8ToUTF16(application_id.origin.host())); |
| - notification_data.direction = |
| - content::PlatformNotificationData::NotificationDirectionLeftToRight; |
| - notification_data.body = |
| - l10n_util::GetStringUTF16(IDS_PUSH_MESSAGING_GENERIC_NOTIFICATION_BODY); |
| - notification_data.tag = |
| - base::ASCIIToUTF16(kPushMessagingForcedNotificationTag); |
| - notification_data.icon = GURL(); // TODO(johnme): Better icon? |
| - notification_service->DisplayPersistentNotification( |
| - profile_, |
| - application_id.service_worker_registration_id, |
| - application_id.origin, |
| - SkBitmap() /* icon */, |
| - notification_data, |
| - content::ChildProcessHost::kInvalidUniqueID /* render_process_id */); |
| -#endif |
| + // Don't track push messages that didn't show a notification but were exempt |
| + // from needing to do so. |
| + if (notification_shown || notification_needed) { |
| + 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.
|
| + std::string updatedData = data.size() >= NOTIFICATION_HISTORY_LENGTH |
| + ? data.substr(1) + new_entry |
| + : data + new_entry; |
| + |
| + content::ServiceWorkerContext* service_worker_context = |
| + content::BrowserContext::GetStoragePartitionForSite( |
| + profile_, application_id.origin)->GetServiceWorkerContext(); |
| + |
| + PushMessagingService::SetNotificationsShownByLastFewPushes( |
| + service_worker_context, application_id.service_worker_registration_id, |
| + application_id.origin, updatedData, |
| + base::Bind(&IgnoreResult)); // This is a heuristic; ignore failure. |
| + } |
| } |
| void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { |