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

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

Issue 842233003: Push API: Require user visible notification, else show auto notification (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@invalid
Patch Set: Created 5 years, 11 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"
11 #include "base/prefs/pref_service.h" 11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h" 12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/notifications/notification_ui_manager.h"
16 #include "chrome/browser/notifications/platform_notification_service_impl.h"
13 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/services/gcm/gcm_profile_service.h" 18 #include "chrome/browser/services/gcm/gcm_profile_service.h"
15 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h" 19 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
16 #include "chrome/browser/services/gcm/push_messaging_application_id.h" 20 #include "chrome/browser/services/gcm/push_messaging_application_id.h"
17 #include "chrome/browser/services/gcm/push_messaging_constants.h" 21 #include "chrome/browser/services/gcm/push_messaging_constants.h"
18 #include "chrome/browser/services/gcm/push_messaging_permission_context.h" 22 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
19 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory. h" 23 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory. h"
20 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/pref_names.h" 25 #include "chrome/common/pref_names.h"
26 #include "components/content_settings/core/browser/host_content_settings_map.h"
27 #include "components/content_settings/core/common/content_settings_types.h"
22 #include "components/content_settings/core/common/permission_request_id.h" 28 #include "components/content_settings/core/common/permission_request_id.h"
23 #include "components/gcm_driver/gcm_driver.h" 29 #include "components/gcm_driver/gcm_driver.h"
24 #include "components/pref_registry/pref_registry_syncable.h" 30 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "content/public/browser/browser_context.h" 31 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/render_frame_host.h" 32 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/web_contents.h" 33 #include "content/public/browser/web_contents.h"
34 #include "content/public/common/child_process_host.h"
35 #include "content/public/common/platform_notification_data.h"
36 #include "third_party/skia/include/core/SkBitmap.h"
28 37
29 namespace gcm { 38 namespace gcm {
30 39
31 namespace { 40 namespace {
32 const int kMaxRegistrations = 1000000; 41 const int kMaxRegistrations = 1000000;
33 42
34 blink::WebPushPermissionStatus ToPushPermission(ContentSetting setting) { 43 blink::WebPushPermissionStatus ToPushPermission(ContentSetting setting) {
35 switch (setting) { 44 switch (setting) {
36 case CONTENT_SETTING_ALLOW: 45 case CONTENT_SETTING_ALLOW:
37 return blink::WebPushPermissionStatusGranted; 46 return blink::WebPushPermissionStatusGranted;
38 case CONTENT_SETTING_BLOCK: 47 case CONTENT_SETTING_BLOCK:
39 return blink::WebPushPermissionStatusDenied; 48 return blink::WebPushPermissionStatusDenied;
40 case CONTENT_SETTING_ASK: 49 case CONTENT_SETTING_ASK:
41 return blink::WebPushPermissionStatusDefault; 50 return blink::WebPushPermissionStatusDefault;
42 default: 51 default:
43 NOTREACHED(); 52 NOTREACHED();
44 return blink::WebPushPermissionStatusDenied; 53 return blink::WebPushPermissionStatusDenied;
45 } 54 }
46 } 55 }
47 56
57 int CountNotifications(Profile* profile, const GURL& origin) {
Peter Beverloo 2015/01/09 17:58:21 CountNotificationsForOrigin.
johnme 2015/01/09 18:28:47 Deleted.
58 return g_browser_process->notification_ui_manager()
59 ->GetAllIdsByProfileAndSourceOrigin(profile, origin)
60 .size();
61 }
62
48 } // namespace 63 } // namespace
49 64
50 // static 65 // static
51 void PushMessagingServiceImpl::RegisterProfilePrefs( 66 void PushMessagingServiceImpl::RegisterProfilePrefs(
52 user_prefs::PrefRegistrySyncable* registry) { 67 user_prefs::PrefRegistrySyncable* registry) {
53 registry->RegisterIntegerPref( 68 registry->RegisterIntegerPref(
54 prefs::kPushMessagingRegistrationCount, 69 prefs::kPushMessagingRegistrationCount,
55 0, 70 0,
56 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 71 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
57 } 72 }
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 // { 160 // {
146 // "registration_ids": ["FOO", "BAR"], 161 // "registration_ids": ["FOO", "BAR"],
147 // "data": { 162 // "data": {
148 // "data": "BAZ", 163 // "data": "BAZ",
149 // }, 164 // },
150 // "delay_while_idle": true, 165 // "delay_while_idle": true,
151 // } 166 // }
152 // TODO(johnme): Make sure this is clearly documented for developers. 167 // TODO(johnme): Make sure this is clearly documented for developers.
153 PushMessagingApplicationId application_id = 168 PushMessagingApplicationId application_id =
154 PushMessagingApplicationId::Parse(app_id); 169 PushMessagingApplicationId::Parse(app_id);
155 DCHECK(application_id.IsValid());
Peter Beverloo 2015/01/09 17:58:20 Why are you removing this DCHECK? You're using the
johnme 2015/01/09 18:28:47 After discussing with Michael, we concluded that m
Peter Beverloo 2015/01/09 18:46:25 No, that's OK. Thanks for the info!
156 GCMClient::MessageData::const_iterator it = message.data.find("data"); 170 GCMClient::MessageData::const_iterator it = message.data.find("data");
157 if (application_id.IsValid() && it != message.data.end()) { 171 if (application_id.IsValid() && it != message.data.end()) {
158 if (!HasPermission(application_id.origin)) { 172 if (!HasPermission(application_id.origin)) {
159 // The |origin| lost push permission. We need to unregister and drop this 173 // The |origin| lost push permission. We need to unregister and drop this
160 // message. 174 // message.
161 Unregister(application_id, UnregisterCallback()); 175 Unregister(application_id, UnregisterCallback());
162 return; 176 return;
163 } 177 }
164 178
165 const std::string& data = it->second; 179 const std::string& data = it->second;
166 content::BrowserContext::DeliverPushMessage( 180 content::BrowserContext::DeliverPushMessage(
167 profile_, 181 profile_,
168 application_id.origin, 182 application_id.origin,
169 application_id.service_worker_registration_id, 183 application_id.service_worker_registration_id,
170 data, 184 data,
171 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback, 185 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
172 weak_factory_.GetWeakPtr(), 186 weak_factory_.GetWeakPtr(),
173 application_id, 187 application_id,
174 message)); 188 message,
189 CountNotifications(profile_, application_id.origin)));
175 } else { 190 } else {
176 // Drop the message, as it is invalid. 191 // Drop the message, as it is invalid.
177 DeliverMessageCallback(application_id, message, 192 DeliverMessageCallback(application_id, message,
193 0 /* notification_count_before_event */,
178 content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE); 194 content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE);
179 } 195 }
180 } 196 }
181 197
182 void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) { 198 void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
183 profile_ = profile; 199 profile_ = profile;
184 } 200 }
185 201
186 void PushMessagingServiceImpl::DeliverMessageCallback( 202 void PushMessagingServiceImpl::DeliverMessageCallback(
187 const PushMessagingApplicationId& application_id, 203 const PushMessagingApplicationId& application_id,
188 const GCMClient::IncomingMessage& message, 204 const GCMClient::IncomingMessage& message,
205 int notification_count_before_event,
189 content::PushDeliveryStatus status) { 206 content::PushDeliveryStatus status) {
190 // TODO(mvanouwerkerk): UMA logging. 207 // TODO(mvanouwerkerk): UMA logging.
191 // TODO(mvanouwerkerk): Show a warning in the developer console of the 208 // TODO(mvanouwerkerk): Show a warning in the developer console of the
192 // Service Worker corresponding to app_id (and/or on an internals page). 209 // Service Worker corresponding to app_id (and/or on an internals page).
193 // TODO(mvanouwerkerk): Is there a way to recover from failure? 210 // TODO(mvanouwerkerk): Is there a way to recover from failure?
194 switch (status) { 211 switch (status) {
212 // Call RequireUserVisibleUX if the message was delivered to the Service
213 // Worker JS, even if the website's event handler failed (to prevent sites
214 // deliberately failing to avoid having to show notifications).
195 case content::PUSH_DELIVERY_STATUS_SUCCESS: 215 case content::PUSH_DELIVERY_STATUS_SUCCESS:
216 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
217 RequireUserVisibleUX(application_id, notification_count_before_event);
218 break;
196 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE: 219 case content::PUSH_DELIVERY_STATUS_INVALID_MESSAGE:
197 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR: 220 case content::PUSH_DELIVERY_STATUS_SERVICE_WORKER_ERROR:
198 case content::PUSH_DELIVERY_STATUS_EVENT_WAITUNTIL_REJECTED:
199 break; 221 break;
200 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER: 222 case content::PUSH_DELIVERY_STATUS_NO_SERVICE_WORKER:
201 Unregister(application_id, UnregisterCallback()); 223 Unregister(application_id, UnregisterCallback());
202 break; 224 break;
203 } 225 }
204 } 226 }
205 227
228 void PushMessagingServiceImpl::RequireUserVisibleUX(
229 const PushMessagingApplicationId& application_id,
230 int notification_count_before_event) {
231 #if defined(ENABLE_NOTIFICATIONS)
232 // Require the event handler to have shown a new notification, or left a
233 // pre-existing one showing (possibly updated), or closed a pre-existing one.
234 // Showing then immediately closing a notification is not sufficient.
Peter Beverloo 2015/01/09 17:58:20 This is a comment that should live in the header f
johnme 2015/01/09 18:28:47 Done. On 2015/01/09 17:58:20, Peter Beverloo wrot
235 if (notification_count_before_event > 0)
236 return; // The requirement is met by a pre-existing notification.
Peter Beverloo 2015/01/09 17:58:21 Really? That would allow me to do a cycle like: [
johnme 2015/01/09 18:28:47 We want to support e.g. dismissing a notification
Peter Beverloo 2015/01/09 18:46:25 Smells like a cat-and-mouse game. We should consid
237 if (CountNotifications(profile_, application_id.origin) > 0)
238 return; // The requirement is met by a newly shown notification.
239
240 // If we haven't returned yet, the site failed to show a notification, so we
241 // show a generic notification. See https://github.com/w3c/push-api/pull/87
Peter Beverloo 2015/01/09 17:58:21 I would very much prefer not to refer to pull requ
johnme 2015/01/09 18:28:47 Done.
242 // TODO(johnme): i18n.
243 content::PlatformNotificationData notification_data;
244 notification_data.title = base::ASCIIToUTF16("Updates available");
245 notification_data.direction =
246 content::PlatformNotificationData::NotificationDirectionLeftToRight;
247 notification_data.lang = "";
248 notification_data.body =
249 base::ASCIIToUTF16("This site updated in the background");
250 notification_data.tag =
251 base::ASCIIToUTF16("user_visible_auto_notification");
252 notification_data.icon = GURL("about:blank");
Peter Beverloo 2015/01/09 17:58:20 GURL()
johnme 2015/01/09 18:28:47 Done.
253 PlatformNotificationServiceImpl::GetInstance()->DisplayPersistentNotification(
254 profile_,
255 application_id.service_worker_registration_id,
256 application_id.origin,
257 SkBitmap() /* icon */,
258 notification_data,
259 content::ChildProcessHost::kInvalidUniqueID /* render_process_id */);
Peter Beverloo 2015/01/09 17:58:20 This is a bit awkward, but OK.
johnme 2015/01/09 18:28:47 Acknowledged.
260 #endif
261 }
262
206 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { 263 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
207 // TODO(mvanouwerkerk): Fire push error event on the Service Worker 264 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
208 // corresponding to app_id. 265 // corresponding to app_id.
209 } 266 }
210 267
211 void PushMessagingServiceImpl::OnSendError( 268 void PushMessagingServiceImpl::OnSendError(
212 const std::string& app_id, 269 const std::string& app_id,
213 const GCMClient::SendErrorDetails& send_error_details) { 270 const GCMClient::SendErrorDetails& send_error_details) {
214 NOTREACHED() << "The Push API shouldn't have sent messages upstream"; 271 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
215 } 272 }
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 } 515 }
459 516
460 void PushMessagingServiceImpl::AddAppHandlerIfNecessary() { 517 void PushMessagingServiceImpl::AddAppHandlerIfNecessary() {
461 if (gcm_profile_service_->driver()->GetAppHandler( 518 if (gcm_profile_service_->driver()->GetAppHandler(
462 kPushMessagingApplicationIdPrefix) != this) 519 kPushMessagingApplicationIdPrefix) != this)
463 gcm_profile_service_->driver()->AddAppHandler( 520 gcm_profile_service_->driver()->AddAppHandler(
464 kPushMessagingApplicationIdPrefix, this); 521 kPushMessagingApplicationIdPrefix, this);
465 } 522 }
466 523
467 } // namespace gcm 524 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698