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/common/chrome_switches.h" 17 #include "chrome/common/pref_names.h" 18 #include "components/gcm_driver/gcm_driver.h" 19 #include "components/pref_registry/pref_registry_syncable.h" 20 21 namespace gcm { 22 23 namespace { 24 const char kAppIdPrefix[] = "push:"; 25 const int kMaxRegistrations = 1000000; 26 } // namespace 27 28 // static 29 void PushMessagingServiceImpl::RegisterProfilePrefs( 30 user_prefs::PrefRegistrySyncable* registry) { 31 registry->RegisterIntegerPref( 32 prefs::kPushMessagingRegistrationCount, 33 0, 34 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 35 } 36 37 // static 38 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) { 39 // TODO(mvanouwerkerk): Make sure to remove this check at the same time as 40 // push graduates from experimental in Blink. 41 if (!CommandLine::ForCurrentProcess()->HasSwitch( 42 switches::kEnableExperimentalWebPlatformFeatures)) { 43 return; 44 } 45 // TODO(johnme): Consider whether push should be enabled in incognito. If it 46 // does get enabled, then be careful that you're reading the pref from the 47 // right profile, as prefs defined in a regular profile are visible in the 48 // corresponding incognito profile unless overrriden. 49 if (!profile || profile->IsOffTheRecord() || 50 profile->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount) <= 51 0) { 52 return; 53 } 54 // Create the GCMProfileService, and hence instantiate this class. 55 GCMProfileService* gcm_service = 56 gcm::GCMProfileServiceFactory::GetForProfile(profile); 57 PushMessagingServiceImpl* push_service = 58 static_cast<PushMessagingServiceImpl*>( 59 gcm_service->push_messaging_service()); 60 // Register ourselves as an app handler. 61 gcm_service->driver()->AddAppHandler(kAppIdPrefix, push_service); 62 } 63 64 PushMessagingServiceImpl::PushMessagingServiceImpl( 65 GCMProfileService* gcm_profile_service, 66 Profile* profile) 67 : gcm_profile_service_(gcm_profile_service), 68 profile_(profile), 69 weak_factory_(this) { 70 } 71 72 PushMessagingServiceImpl::~PushMessagingServiceImpl() { 73 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver, 74 // then we should call RemoveAppHandler. 75 } 76 77 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const { 78 // TODO(mvanouwerkerk): Finalize and centralize format of Push API app_id. 79 return StartsWithASCII(app_id, kAppIdPrefix, true); 80 } 81 82 void PushMessagingServiceImpl::ShutdownHandler() { 83 // TODO(johnme): Do any necessary cleanup. 84 } 85 86 void PushMessagingServiceImpl::OnMessage( 87 const std::string& app_id, 88 const GCMClient::IncomingMessage& message) { 89 // The Push API only exposes a single string of data in the push event fired 90 // on the Service Worker. When developers send messages using GCM to the Push 91 // API, they must pass a single key-value pair, where the key is "data" and 92 // the value is the string they want to be passed to their Service Worker. 93 // For example, they could send the following JSON using the HTTPS GCM API: 94 // { 95 // "registration_ids": ["FOO", "BAR"], 96 // "data": { 97 // "data": "BAZ", 98 // }, 99 // "delay_while_idle": true, 100 // } 101 // TODO(johnme): Make sure this is clearly documented for developers. 102 GCMClient::MessageData::const_iterator it = message.data.find("data"); 103 if (it != message.data.end()) { 104 const std::string& data ALLOW_UNUSED = it->second; 105 // TODO(mvanouwerkerk): Fire push event with data on the Service Worker 106 // corresponding to app_id (and remove ALLOW_UNUSED above). 107 } else { 108 // Drop the message, as it is invalid. 109 // TODO(mvanouwerkerk): Show a warning in the developer console of the 110 // Service Worker corresponding to app_id. 111 // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page) 112 // to know when bad things happen. 113 } 114 } 115 116 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { 117 // TODO(mvanouwerkerk): Fire push error event on the Service Worker 118 // corresponding to app_id. 119 } 120 121 void PushMessagingServiceImpl::OnSendError( 122 const std::string& app_id, 123 const GCMClient::SendErrorDetails& send_error_details) { 124 NOTREACHED() << "The Push API shouldn't have sent messages upstream"; 125 } 126 127 void PushMessagingServiceImpl::Register( 128 const std::string& app_id, 129 const std::string& sender_id, 130 const content::PushMessagingService::RegisterCallback& callback) { 131 if (!gcm_profile_service_->driver()) { 132 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?"; 133 } 134 135 if (profile_->GetPrefs()->GetInteger( 136 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) { 137 DidRegister(app_id, callback, "", GCMClient::UNKNOWN_ERROR); 138 return; 139 } 140 141 // If this is registering for the first time then the driver does not have 142 // this as an app handler and registration would fail. 143 if (gcm_profile_service_->driver()->GetAppHandler(kAppIdPrefix) != this) 144 gcm_profile_service_->driver()->AddAppHandler(kAppIdPrefix, this); 145 146 std::vector<std::string> sender_ids(1, sender_id); 147 gcm_profile_service_->driver()->Register( 148 app_id, 149 sender_ids, 150 base::Bind(&PushMessagingServiceImpl::DidRegister, 151 weak_factory_.GetWeakPtr(), 152 app_id, 153 callback)); 154 } 155 156 void PushMessagingServiceImpl::DidRegister( 157 const std::string& app_id, 158 const content::PushMessagingService::RegisterCallback& callback, 159 const std::string& registration_id, 160 GCMClient::Result result) { 161 GURL endpoint = GURL("https://android.googleapis.com/gcm/send"); 162 bool success = (result == GCMClient::SUCCESS); 163 callback.Run(endpoint, registration_id, success); 164 if (success) { 165 // TODO(johnme): Make sure the pref doesn't get out of sync after crashes. 166 int registration_count = profile_->GetPrefs()->GetInteger( 167 prefs::kPushMessagingRegistrationCount); 168 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount, 169 registration_count + 1); 170 } 171 } 172 173 // TODO(johnme): Unregister should decrement the pref, and call 174 // RemoveAppHandler if the count drops to zero. 175 176 } // namespace gcm 177