Move Webstore URL concepts to //extensions and out
[chromium-blink-merge.git] / chrome / browser / services / gcm / push_messaging_service_impl.cc
blobdb9a8a2ef54aa8b746e0281bd333a9fd9947071c
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"
7 #include <vector>
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"
28 namespace gcm {
30 namespace {
31 const int kMaxRegistrations = 1000000;
32 } // namespace
34 // static
35 void PushMessagingServiceImpl::RegisterProfilePrefs(
36 user_prefs::PrefRegistrySyncable* registry) {
37 registry->RegisterIntegerPref(
38 prefs::kPushMessagingRegistrationCount,
40 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
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;
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;
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);
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) {
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:
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.
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?
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(
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?";
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(
176 callback,
177 std::string(),
178 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_LIMIT_REACHED);
179 return;
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);
189 content::RenderFrameHost* render_frame_host =
190 content::RenderFrameHost::FromID(renderer_id, render_frame_id);
192 // The frame doesn't exist any more, or we received a bad frame id.
193 if (!render_frame_host)
194 return;
196 content::WebContents* web_contents =
197 content::WebContents::FromRenderFrameHost(render_frame_host);
199 // The page doesn't exist any more or we got a bad render frame host.
200 if (!web_contents)
201 return;
203 // TODO(miguelg) need to send this over IPC when bubble support is
204 // implemented.
205 int bridge_id = -1;
207 const PermissionRequestID id(
208 renderer_id, web_contents->GetRoutingID(), bridge_id, GURL());
210 GURL embedder = web_contents->GetLastCommittedURL();
211 gcm::PushMessagingPermissionContext* permission_context =
212 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_);
214 if (permission_context == NULL) {
215 RegisterEnd(
216 callback,
217 std::string(),
218 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED);
219 return;
222 permission_context->RequestPermission(
223 web_contents,
225 embedder,
226 user_gesture,
227 base::Bind(&PushMessagingServiceImpl::DidRequestPermission,
228 weak_factory_.GetWeakPtr(),
229 application_id,
230 sender_id,
231 callback));
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);
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);
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;
273 // The GCMDriver could be NULL if GCMProfileService has been shut down.
274 if (!gcm_profile_service_->driver())
275 return;
277 std::vector<std::string> sender_ids(1, sender_id);
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));
287 // TODO(johnme): Unregister should decrement the pref, and call
288 // RemoveAppHandler if the count drops to zero.
290 } // namespace gcm