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.
5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h"
10 #include "base/command_line.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/services/gcm/gcm_profile_service.h"
15 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
16 #include "chrome/browser/services/gcm/push_messaging_application_id.h"
17 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
18 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/pref_names.h"
21 #include "components/content_settings/core/common/permission_request_id.h"
22 #include "components/gcm_driver/gcm_driver.h"
23 #include "components/pref_registry/pref_registry_syncable.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/render_frame_host.h"
26 #include "content/public/browser/web_contents.h"
31 const int kMaxRegistrations
= 1000000;
35 void PushMessagingServiceImpl::RegisterProfilePrefs(
36 user_prefs::PrefRegistrySyncable
* registry
) {
37 registry
->RegisterIntegerPref(
38 prefs::kPushMessagingRegistrationCount
,
40 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
44 void PushMessagingServiceImpl::InitializeForProfile(Profile
* profile
) {
45 // TODO(mvanouwerkerk): Make sure to remove this check at the same time as
46 // push graduates from experimental in Blink.
47 if (!CommandLine::ForCurrentProcess()->HasSwitch(
48 switches::kEnableExperimentalWebPlatformFeatures
)) {
51 // TODO(johnme): Consider whether push should be enabled in incognito. If it
52 // does get enabled, then be careful that you're reading the pref from the
53 // right profile, as prefs defined in a regular profile are visible in the
54 // corresponding incognito profile unless overrriden.
55 if (!profile
|| profile
->IsOffTheRecord() ||
56 profile
->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount
) <=
60 // Create the GCMProfileService, and hence instantiate this class.
61 GCMProfileService
* gcm_service
=
62 gcm::GCMProfileServiceFactory::GetForProfile(profile
);
63 PushMessagingServiceImpl
* push_service
=
64 static_cast<PushMessagingServiceImpl
*>(
65 gcm_service
->push_messaging_service());
66 // Register ourselves as an app handler.
67 gcm_service
->driver()->AddAppHandler(kPushMessagingApplicationIdPrefix
,
71 PushMessagingServiceImpl::PushMessagingServiceImpl(
72 GCMProfileService
* gcm_profile_service
,
74 : gcm_profile_service_(gcm_profile_service
),
79 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
80 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
81 // then we should call RemoveAppHandler.
84 bool PushMessagingServiceImpl::CanHandle(const std::string
& app_id
) const {
85 return PushMessagingApplicationId::Parse(app_id
).IsValid();
88 void PushMessagingServiceImpl::ShutdownHandler() {
89 // TODO(johnme): Do any necessary cleanup.
92 void PushMessagingServiceImpl::OnMessage(
93 const std::string
& app_id
,
94 const GCMClient::IncomingMessage
& message
) {
95 // The Push API only exposes a single string of data in the push event fired
96 // on the Service Worker. When developers send messages using GCM to the Push
97 // API, they must pass a single key-value pair, where the key is "data" and
98 // the value is the string they want to be passed to their Service Worker.
99 // For example, they could send the following JSON using the HTTPS GCM API:
101 // "registration_ids": ["FOO", "BAR"],
105 // "delay_while_idle": true,
107 // TODO(johnme): Make sure this is clearly documented for developers.
108 PushMessagingApplicationId application_id
=
109 PushMessagingApplicationId::Parse(app_id
);
110 DCHECK(application_id
.IsValid());
111 GCMClient::MessageData::const_iterator it
= message
.data
.find("data");
112 if (application_id
.IsValid() && it
!= message
.data
.end()) {
113 const std::string
& data
= it
->second
;
114 content::BrowserContext::DeliverPushMessage(
116 application_id
.origin
,
117 application_id
.service_worker_registration_id
,
119 base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback
,
120 weak_factory_
.GetWeakPtr(),
124 // Drop the message, as it is invalid.
125 // TODO(mvanouwerkerk): Show a warning in the developer console of the
126 // Service Worker corresponding to app_id.
127 // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page)
128 // to know when bad things happen.
132 void PushMessagingServiceImpl::DeliverMessageCallback(
133 const PushMessagingApplicationId
& application_id
,
134 const GCMClient::IncomingMessage
& message
,
135 content::PushDeliveryStatus status
) {
136 // TODO(mvanouwerkerk): UMA logging.
137 // TODO(mvanouwerkerk): Is there a way to recover from failure?
140 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string
& app_id
) {
141 // TODO(mvanouwerkerk): Fire push error event on the Service Worker
142 // corresponding to app_id.
145 void PushMessagingServiceImpl::OnSendError(
146 const std::string
& app_id
,
147 const GCMClient::SendErrorDetails
& send_error_details
) {
148 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
151 void PushMessagingServiceImpl::OnSendAcknowledged(
152 const std::string
& app_id
,
153 const std::string
& message_id
) {
154 NOTREACHED() << "The Push API shouldn't have sent messages upstream";
157 void PushMessagingServiceImpl::Register(
159 int64 service_worker_registration_id
,
160 const std::string
& sender_id
,
164 const content::PushMessagingService::RegisterCallback
& callback
) {
165 if (!gcm_profile_service_
->driver()) {
166 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
169 PushMessagingApplicationId application_id
=
170 PushMessagingApplicationId(origin
, service_worker_registration_id
);
171 DCHECK(application_id
.IsValid());
173 if (profile_
->GetPrefs()->GetInteger(
174 prefs::kPushMessagingRegistrationCount
) >= kMaxRegistrations
) {
175 RegisterEnd(callback
,
177 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED
);
181 // If this is registering for the first time then the driver does not have
182 // this as an app handler and registration would fail.
183 if (gcm_profile_service_
->driver()->GetAppHandler(
184 kPushMessagingApplicationIdPrefix
) != this)
185 gcm_profile_service_
->driver()->AddAppHandler(
186 kPushMessagingApplicationIdPrefix
, this);
188 content::RenderFrameHost
* render_frame_host
=
189 content::RenderFrameHost::FromID(renderer_id
, render_frame_id
);
191 // The frame doesn't exist any more, or we received a bad frame id.
192 if (!render_frame_host
)
195 content::WebContents
* web_contents
=
196 content::WebContents::FromRenderFrameHost(render_frame_host
);
198 // The page doesn't exist any more or we got a bad render frame host.
202 // TODO(miguelg) need to send this over IPC when bubble support is
206 const PermissionRequestID
id(
207 renderer_id
, web_contents
->GetRoutingID(), bridge_id
, GURL());
209 GURL embedder
= web_contents
->GetLastCommittedURL();
210 gcm::PushMessagingPermissionContext
* permission_context
=
211 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_
);
213 if (permission_context
== NULL
) {
214 RegisterEnd(callback
,
216 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
220 permission_context
->RequestPermission(
225 base::Bind(&PushMessagingServiceImpl::DidRequestPermission
,
226 weak_factory_
.GetWeakPtr(),
232 void PushMessagingServiceImpl::RegisterEnd(
233 const content::PushMessagingService::RegisterCallback
& callback
,
234 const std::string
& registration_id
,
235 content::PushRegistrationStatus status
) {
236 GURL endpoint
= GURL("https://android.googleapis.com/gcm/send");
237 callback
.Run(endpoint
, registration_id
, status
);
238 if (status
== content::PUSH_REGISTRATION_STATUS_SUCCESS
) {
239 // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
240 int registration_count
= profile_
->GetPrefs()->GetInteger(
241 prefs::kPushMessagingRegistrationCount
);
242 profile_
->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount
,
243 registration_count
+ 1);
247 void PushMessagingServiceImpl::DidRegister(
248 const content::PushMessagingService::RegisterCallback
& callback
,
249 const std::string
& registration_id
,
250 GCMClient::Result result
) {
251 content::PushRegistrationStatus status
=
252 result
== GCMClient::SUCCESS
253 ? content::PUSH_REGISTRATION_STATUS_SUCCESS
254 : content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR
;
255 RegisterEnd(callback
, registration_id
, status
);
258 void PushMessagingServiceImpl::DidRequestPermission(
259 const PushMessagingApplicationId
& application_id
,
260 const std::string
& sender_id
,
261 const content::PushMessagingService::RegisterCallback
& register_callback
,
264 RegisterEnd(register_callback
,
266 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED
);
270 // The GCMDriver could be NULL if GCMProfileService has been shut down.
271 if (!gcm_profile_service_
->driver())
274 std::vector
<std::string
> sender_ids(1, sender_id
);
276 gcm_profile_service_
->driver()->Register(
277 application_id
.ToString(),
279 base::Bind(&PushMessagingServiceImpl::DidRegister
,
280 weak_factory_
.GetWeakPtr(),
284 // TODO(johnme): Unregister should decrement the pref, and call
285 // RemoveAppHandler if the count drops to zero.