Upstream version 9.38.198.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/content_settings/permission_request_id.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/services/gcm/gcm_profile_service.h"
16 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
17 #include "chrome/browser/services/gcm/push_messaging_application_id.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/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"
27
28 namespace gcm {
29
30 namespace {
31 const int kMaxRegistrations = 1000000;
32 }  // namespace
33
34 // static
35 void PushMessagingServiceImpl::RegisterProfilePrefs(
36     user_prefs::PrefRegistrySyncable* registry) {
37   registry->RegisterIntegerPref(
38       prefs::kPushMessagingRegistrationCount,
39       0,
40       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
41 }
42
43 // static
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)) {
49     return;
50   }
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) <=
57           0) {
58     return;
59   }
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,
68                                        push_service);
69 }
70
71 PushMessagingServiceImpl::PushMessagingServiceImpl(
72     GCMProfileService* gcm_profile_service,
73     Profile* profile)
74     : gcm_profile_service_(gcm_profile_service),
75       profile_(profile),
76       weak_factory_(this) {
77 }
78
79 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
80   // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
81   // then we should call RemoveAppHandler.
82 }
83
84 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
85   return PushMessagingApplicationId::Parse(app_id).IsValid();
86 }
87
88 void PushMessagingServiceImpl::ShutdownHandler() {
89   // TODO(johnme): Do any necessary cleanup.
90 }
91
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:
100   // {
101   //     "registration_ids": ["FOO", "BAR"],
102   //     "data": {
103   //         "data": "BAZ",
104   //     },
105   //     "delay_while_idle": true,
106   // }
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(
115         profile_,
116         application_id.origin,
117         application_id.service_worker_registration_id,
118         data,
119         base::Bind(&PushMessagingServiceImpl::DeliverMessageCallback,
120                    weak_factory_.GetWeakPtr(),
121                    application_id,
122                    message));
123   } else {
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.
129   }
130 }
131
132 void PushMessagingServiceImpl::DeliverMessageCallback(
133     const PushMessagingApplicationId& application_id,
134     const GCMClient::IncomingMessage& message,
135     content::PushMessagingStatus status) {
136   // TODO(mvanouwerkerk): UMA logging.
137   // TODO(mvanouwerkerk): Is there a way to recover from failure?
138 }
139
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.
143 }
144
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";
149 }
150
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";
155 }
156
157 void PushMessagingServiceImpl::Register(
158     const GURL& origin,
159     int64 service_worker_registration_id,
160     const std::string& sender_id,
161     int renderer_id,
162     int render_frame_id,
163     bool user_gesture,
164     const content::PushMessagingService::RegisterCallback& callback) {
165   if (!gcm_profile_service_->driver()) {
166     NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
167   }
168
169   PushMessagingApplicationId application_id =
170       PushMessagingApplicationId(origin, service_worker_registration_id);
171   DCHECK(application_id.IsValid());
172
173   if (profile_->GetPrefs()->GetInteger(
174           prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
175     RegisterEnd(
176         callback,
177         std::string(),
178         content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_LIMIT_REACHED);
179     return;
180   }
181
182   // If this is registering for the first time then the driver does not have
183   // this as an app handler and registration would fail.
184   if (gcm_profile_service_->driver()->GetAppHandler(
185           kPushMessagingApplicationIdPrefix) != this)
186     gcm_profile_service_->driver()->AddAppHandler(
187         kPushMessagingApplicationIdPrefix, this);
188
189   content::RenderFrameHost* render_frame_host =
190       content::RenderFrameHost::FromID(renderer_id, render_frame_id);
191
192   // The frame doesn't exist any more, or we received a bad frame id.
193   if (!render_frame_host)
194     return;
195
196   content::WebContents* web_contents =
197       content::WebContents::FromRenderFrameHost(render_frame_host);
198
199   // The page doesn't exist any more or we got a bad render frame host.
200   if (!web_contents)
201     return;
202
203   // TODO(miguelg) need to send this over IPC when bubble support is
204   // implemented.
205   int bridge_id = -1;
206
207   const PermissionRequestID id(
208       renderer_id, web_contents->GetRoutingID(), bridge_id, GURL());
209
210   GURL embedder = web_contents->GetLastCommittedURL();
211   gcm::PushMessagingPermissionContext* permission_context =
212       gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
213
214   if (permission_context == NULL) {
215     RegisterEnd(
216         callback,
217         std::string(),
218         content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED);
219     return;
220   }
221
222   permission_context->RequestPermission(
223       web_contents,
224       id,
225       embedder,
226       user_gesture,
227       base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
228                  weak_factory_.GetWeakPtr(),
229                  application_id,
230                  sender_id,
231                  callback));
232 }
233
234 void PushMessagingServiceImpl::RegisterEnd(
235     const content::PushMessagingService::RegisterCallback& callback,
236     const std::string& registration_id,
237     content::PushMessagingStatus status) {
238   GURL endpoint = GURL("https://android.googleapis.com/gcm/send");
239   callback.Run(endpoint, registration_id, status);
240   if (status == content::PUSH_MESSAGING_STATUS_OK) {
241     // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
242     int registration_count = profile_->GetPrefs()->GetInteger(
243         prefs::kPushMessagingRegistrationCount);
244     profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
245                                      registration_count + 1);
246   }
247 }
248
249 void PushMessagingServiceImpl::DidRegister(
250     const content::PushMessagingService::RegisterCallback& callback,
251     const std::string& registration_id,
252     GCMClient::Result result) {
253   content::PushMessagingStatus status =
254       result == GCMClient::SUCCESS
255           ? content::PUSH_MESSAGING_STATUS_OK
256           : content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_SERVICE_ERROR;
257   RegisterEnd(callback, registration_id, status);
258 }
259
260 void PushMessagingServiceImpl::DidRequestPermission(
261     const PushMessagingApplicationId& application_id,
262     const std::string& sender_id,
263     const content::PushMessagingService::RegisterCallback& register_callback,
264     bool allow) {
265   if (!allow) {
266     RegisterEnd(
267         register_callback,
268         std::string(),
269         content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED);
270     return;
271   }
272
273   // The GCMDriver could be NULL if GCMProfileService has been shut down.
274   if (!gcm_profile_service_->driver())
275     return;
276
277   std::vector<std::string> sender_ids(1, sender_id);
278
279   gcm_profile_service_->driver()->Register(
280       application_id.ToString(),
281       sender_ids,
282       base::Bind(&PushMessagingServiceImpl::DidRegister,
283                  weak_factory_.GetWeakPtr(),
284                  register_callback));
285 }
286
287 // TODO(johnme): Unregister should decrement the pref, and call
288 // RemoveAppHandler if the count drops to zero.
289
290 }  // namespace gcm