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_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" 27 28namespace gcm { 29 30namespace { 31const int kMaxRegistrations = 1000000; 32} // namespace 33 34// static 35void 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 44void 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 71PushMessagingServiceImpl::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 79PushMessagingServiceImpl::~PushMessagingServiceImpl() { 80 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver, 81 // then we should call RemoveAppHandler. 82} 83 84bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const { 85 return PushMessagingApplicationId::Parse(app_id).IsValid(); 86} 87 88void PushMessagingServiceImpl::ShutdownHandler() { 89 // TODO(johnme): Do any necessary cleanup. 90} 91 92void 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 132void 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 140void 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 145void 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 151void 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 157void 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 234void 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 249void 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 260void 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 291