Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / services / gcm / push_messaging_service_impl.cc
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.
4
5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h"
6
7 #include <vector>
8
9 #include "base/bind.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_constants.h"
18 #include "chrome/browser/services/gcm/push_messaging_permission_context.h"
19 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/content_settings/core/common/permission_request_id.h"
23 #include "components/gcm_driver/gcm_driver.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/render_frame_host.h"
27 #include "content/public/browser/web_contents.h"
28
29 namespace gcm {
30
31 namespace {
32 const int kMaxRegistrations = 1000000;
33
34 blink::WebPushPermissionStatus ToPushPermission(ContentSetting setting) {
35   switch (setting) {
36     case CONTENT_SETTING_ALLOW:
37       return blink::WebPushPermissionStatusGranted;
38     case CONTENT_SETTING_BLOCK:
39       return blink::WebPushPermissionStatusDenied;
40     case CONTENT_SETTING_ASK:
41       return blink::WebPushPermissionStatusDefault;
42     default:
43       NOTREACHED();
44       return blink::WebPushPermissionStatusDenied;
45   }
46 }
47
48 }  // namespace
49
50 // static
51 void PushMessagingServiceImpl::RegisterProfilePrefs(
52     user_prefs::PrefRegistrySyncable* registry) {
53   registry->RegisterIntegerPref(
54       prefs::kPushMessagingRegistrationCount,
55       0,
56       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
57 }
58
59 // static
60 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
61   // TODO(mvanouwerkerk): Make sure to remove this check at the same time as
62   // push graduates from experimental in Blink.
63   if (!CommandLine::ForCurrentProcess()->HasSwitch(
64           switches::kEnableExperimentalWebPlatformFeatures)) {
65     return;
66   }
67   // TODO(johnme): Consider whether push should be enabled in incognito. If it
68   // does get enabled, then be careful that you're reading the pref from the
69   // right profile, as prefs defined in a regular profile are visible in the
70   // corresponding incognito profile unless overrriden.
71   if (!profile || profile->IsOffTheRecord() ||
72       profile->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount) <=
73           0) {
74     return;
75   }
76   // Create the GCMProfileService, and hence instantiate this class.
77   GCMProfileService* gcm_service =
78       gcm::GCMProfileServiceFactory::GetForProfile(profile);
79   PushMessagingServiceImpl* push_service =
80       static_cast<PushMessagingServiceImpl*>(
81           gcm_service->push_messaging_service());
82   // Register ourselves as an app handler.
83   gcm_service->driver()->AddAppHandler(kPushMessagingApplicationIdPrefix,
84                                        push_service);
85 }
86
87 PushMessagingServiceImpl::PushMessagingServiceImpl(
88     GCMProfileService* gcm_profile_service,
89     Profile* profile)
90     : gcm_profile_service_(gcm_profile_service),
91       profile_(profile),
92       weak_factory_(this) {
93 }
94
95 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
96   // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
97   // then we should call RemoveAppHandler.
98 }
99
100 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
101   return PushMessagingApplicationId::Parse(app_id).IsValid();
102 }
103
104 void PushMessagingServiceImpl::ShutdownHandler() {
105   // TODO(johnme): Do any necessary cleanup.
106 }
107
108 void PushMessagingServiceImpl::OnMessage(
109     const std::string& app_id,
110     const GCMClient::IncomingMessage& message) {
111   // The Push API only exposes a single string of data in the push event fired
112   // on the Service Worker. When developers send messages using GCM to the Push
113   // API, they must pass a single key-value pair, where the key is "data" and
114   // the value is the string they want to be passed to their Service Worker.
115   // For example, they could send the following JSON using the HTTPS GCM API:
116   // {
117   //     "registration_ids": ["FOO", "BAR"],
118   //     "data": {
119   //         "data": "BAZ",
120   //     },
121   //     "delay_while_idle": true,
122   // }
123   // TODO(johnme): Make sure this is clearly documented for developers.
124   PushMessagingApplicationId application_id =
125       PushMessagingApplicationId::Parse(app_id);
126   DCHECK(application_id.IsValid());
127   GCMClient::MessageData::const_iterator it = message.data.find("data");
128   if (application_id.IsValid() && it != message.data.end()) {
129     const std::string& data = it->second;
130     content::BrowserContext::DeliverPushMessage(
131         profile_,
132         application_id.origin,
133         application_id.service_worker_registration_id,
134         data,
135         base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
136                    weak_factory_.GetWeakPtr(),
137                    application_id,
138                    message));
139   } else {
140     // Drop the message, as it is invalid.
141     // TODO(mvanouwerkerk): Show a warning in the developer console of the
142     // Service Worker corresponding to app_id.
143     // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page)
144     // to know when bad things happen.
145   }
146 }
147
148 void PushMessagingServiceImpl::SetProfileForTesting(Profile* profile) {
149   profile_ = profile;
150 }
151
152 void PushMessagingServiceImpl::DeliverMessageCallback(
153     const PushMessagingApplicationId& application_id,
154     const GCMClient::IncomingMessage& message,
155     content::PushDeliveryStatus status) {
156   // TODO(mvanouwerkerk): UMA logging.
157   // TODO(mvanouwerkerk): Is there a way to recover from failure?
158 }
159
160 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
161   // TODO(mvanouwerkerk): Fire push error event on the Service Worker
162   // corresponding to app_id.
163 }
164
165 void PushMessagingServiceImpl::OnSendError(
166     const std::string& app_id,
167     const GCMClient::SendErrorDetails& send_error_details) {
168   NOTREACHED() << "The Push API shouldn't have sent messages upstream";
169 }
170
171 void PushMessagingServiceImpl::OnSendAcknowledged(
172     const std::string& app_id,
173     const std::string& message_id) {
174   NOTREACHED() << "The Push API shouldn't have sent messages upstream";
175 }
176
177 void PushMessagingServiceImpl::Register(
178     const GURL& origin,
179     int64 service_worker_registration_id,
180     const std::string& sender_id,
181     int renderer_id,
182     int render_frame_id,
183     bool user_gesture,
184     const content::PushMessagingService::RegisterCallback& callback) {
185   if (!gcm_profile_service_->driver()) {
186     NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
187   }
188
189   PushMessagingApplicationId application_id =
190       PushMessagingApplicationId(origin, service_worker_registration_id);
191   DCHECK(application_id.IsValid());
192
193   if (profile_->GetPrefs()->GetInteger(
194           prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
195     RegisterEnd(callback,
196                 std::string(),
197                 content::PUSH_REGISTRATION_STATUS_LIMIT_REACHED);
198     return;
199   }
200
201   // If this is registering for the first time then the driver does not have
202   // this as an app handler and registration would fail.
203   if (gcm_profile_service_->driver()->GetAppHandler(
204           kPushMessagingApplicationIdPrefix) != this)
205     gcm_profile_service_->driver()->AddAppHandler(
206         kPushMessagingApplicationIdPrefix, this);
207
208   content::RenderFrameHost* render_frame_host =
209       content::RenderFrameHost::FromID(renderer_id, render_frame_id);
210
211   // The frame doesn't exist any more, or we received a bad frame id.
212   if (!render_frame_host)
213     return;
214
215   content::WebContents* web_contents =
216       content::WebContents::FromRenderFrameHost(render_frame_host);
217
218   // The page doesn't exist any more or we got a bad render frame host.
219   if (!web_contents)
220     return;
221
222   // TODO(miguelg) need to send this over IPC when bubble support is
223   // implemented.
224   int bridge_id = -1;
225
226   const PermissionRequestID id(
227       renderer_id, web_contents->GetRoutingID(), bridge_id, GURL());
228
229   GURL embedder = web_contents->GetLastCommittedURL();
230   gcm::PushMessagingPermissionContext* permission_context =
231       gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
232
233   if (permission_context == NULL) {
234     RegisterEnd(callback,
235                 std::string(),
236                 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
237     return;
238   }
239
240   permission_context->RequestPermission(
241       web_contents,
242       id,
243       embedder,
244       user_gesture,
245       base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
246                  weak_factory_.GetWeakPtr(),
247                  application_id,
248                  sender_id,
249                  callback));
250 }
251
252 blink::WebPushPermissionStatus PushMessagingServiceImpl::GetPermissionStatus(
253     const GURL& requesting_origin,
254     int renderer_id,
255     int render_frame_id) {
256   content::RenderFrameHost* render_frame_host =
257       content::RenderFrameHost::FromID(renderer_id, render_frame_id);
258
259   // The frame doesn't exist any more, or we received a bad frame id.
260   if (!render_frame_host)
261     return blink::WebPushPermissionStatusDenied;
262
263   content::WebContents* web_contents =
264       content::WebContents::FromRenderFrameHost(render_frame_host);
265
266   // The page doesn't exist any more or we got a bad render frame host.
267   if (!web_contents)
268     return blink::WebPushPermissionStatusDenied;
269
270   GURL embedder_origin = web_contents->GetLastCommittedURL().GetOrigin();
271   gcm::PushMessagingPermissionContext* permission_context =
272       gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
273   return ToPushPermission(
274       permission_context->GetPermissionStatus(
275           requesting_origin, embedder_origin));
276 }
277
278 void PushMessagingServiceImpl::RegisterEnd(
279     const content::PushMessagingService::RegisterCallback& callback,
280     const std::string& registration_id,
281     content::PushRegistrationStatus status) {
282   GURL endpoint = GURL(std::string(kPushMessagingEndpoint));
283   callback.Run(endpoint, registration_id, status);
284   if (status == content::PUSH_REGISTRATION_STATUS_SUCCESS) {
285     // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
286     int registration_count = profile_->GetPrefs()->GetInteger(
287         prefs::kPushMessagingRegistrationCount);
288     profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
289                                      registration_count + 1);
290   }
291 }
292
293 void PushMessagingServiceImpl::DidRegister(
294     const content::PushMessagingService::RegisterCallback& callback,
295     const std::string& registration_id,
296     GCMClient::Result result) {
297   content::PushRegistrationStatus status =
298       result == GCMClient::SUCCESS
299           ? content::PUSH_REGISTRATION_STATUS_SUCCESS
300           : content::PUSH_REGISTRATION_STATUS_SERVICE_ERROR;
301   RegisterEnd(callback, registration_id, status);
302 }
303
304 void PushMessagingServiceImpl::DidRequestPermission(
305     const PushMessagingApplicationId& application_id,
306     const std::string& sender_id,
307     const content::PushMessagingService::RegisterCallback& register_callback,
308     bool allow) {
309   if (!allow) {
310     RegisterEnd(register_callback,
311                 std::string(),
312                 content::PUSH_REGISTRATION_STATUS_PERMISSION_DENIED);
313     return;
314   }
315
316   // The GCMDriver could be NULL if GCMProfileService has been shut down.
317   if (!gcm_profile_service_->driver())
318     return;
319
320   std::vector<std::string> sender_ids(1, sender_id);
321
322   gcm_profile_service_->driver()->Register(
323       application_id.ToString(),
324       sender_ids,
325       base::Bind(&PushMessagingServiceImpl::DidRegister,
326                  weak_factory_.GetWeakPtr(),
327                  register_callback));
328 }
329
330 // TODO(johnme): Unregister should decrement the pref, and call
331 // RemoveAppHandler if the count drops to zero.
332
333 }  // namespace gcm