OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h" | 5 #include "chrome/browser/services/gcm/push_messaging_service_impl.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/prefs/pref_service.h" | 11 #include "base/prefs/pref_service.h" |
12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
13 #include "chrome/browser/content_settings/permission_request_id.h" | 13 #include "chrome/browser/content_settings/permission_request_id.h" |
14 #include "chrome/browser/profiles/profile.h" | 14 #include "chrome/browser/profiles/profile.h" |
15 #include "chrome/browser/services/gcm/gcm_profile_service.h" | 15 #include "chrome/browser/services/gcm/gcm_profile_service.h" |
16 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h" | 16 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h" |
| 17 #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.h" |
18 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory.
h" | 19 #include "chrome/browser/services/gcm/push_messaging_permission_context_factory.
h" |
19 #include "chrome/common/chrome_switches.h" | 20 #include "chrome/common/chrome_switches.h" |
20 #include "chrome/common/pref_names.h" | 21 #include "chrome/common/pref_names.h" |
21 #include "components/gcm_driver/gcm_driver.h" | 22 #include "components/gcm_driver/gcm_driver.h" |
22 #include "components/pref_registry/pref_registry_syncable.h" | 23 #include "components/pref_registry/pref_registry_syncable.h" |
23 #include "content/public/browser/render_frame_host.h" | 24 #include "content/public/browser/render_frame_host.h" |
24 #include "content/public/browser/web_contents.h" | 25 #include "content/public/browser/web_contents.h" |
25 | 26 |
26 namespace gcm { | 27 namespace gcm { |
27 | 28 |
28 namespace { | 29 namespace { |
29 const char kAppIdPrefix[] = "push:"; | |
30 const int kMaxRegistrations = 1000000; | 30 const int kMaxRegistrations = 1000000; |
31 } // namespace | 31 } // namespace |
32 | 32 |
33 // static | 33 // static |
34 void PushMessagingServiceImpl::RegisterProfilePrefs( | 34 void PushMessagingServiceImpl::RegisterProfilePrefs( |
35 user_prefs::PrefRegistrySyncable* registry) { | 35 user_prefs::PrefRegistrySyncable* registry) { |
36 registry->RegisterIntegerPref( | 36 registry->RegisterIntegerPref( |
37 prefs::kPushMessagingRegistrationCount, | 37 prefs::kPushMessagingRegistrationCount, |
38 0, | 38 0, |
39 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | 39 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
(...skipping 16 matching lines...) Expand all Loading... |
56 0) { | 56 0) { |
57 return; | 57 return; |
58 } | 58 } |
59 // Create the GCMProfileService, and hence instantiate this class. | 59 // Create the GCMProfileService, and hence instantiate this class. |
60 GCMProfileService* gcm_service = | 60 GCMProfileService* gcm_service = |
61 gcm::GCMProfileServiceFactory::GetForProfile(profile); | 61 gcm::GCMProfileServiceFactory::GetForProfile(profile); |
62 PushMessagingServiceImpl* push_service = | 62 PushMessagingServiceImpl* push_service = |
63 static_cast<PushMessagingServiceImpl*>( | 63 static_cast<PushMessagingServiceImpl*>( |
64 gcm_service->push_messaging_service()); | 64 gcm_service->push_messaging_service()); |
65 // Register ourselves as an app handler. | 65 // Register ourselves as an app handler. |
66 gcm_service->driver()->AddAppHandler(kAppIdPrefix, push_service); | 66 gcm_service->driver()->AddAppHandler(kPushMessagingApplicationIdPrefix, |
| 67 push_service); |
67 } | 68 } |
68 | 69 |
69 PushMessagingServiceImpl::PushMessagingServiceImpl( | 70 PushMessagingServiceImpl::PushMessagingServiceImpl( |
70 GCMProfileService* gcm_profile_service, | 71 GCMProfileService* gcm_profile_service, |
71 Profile* profile) | 72 Profile* profile) |
72 : gcm_profile_service_(gcm_profile_service), | 73 : gcm_profile_service_(gcm_profile_service), |
73 profile_(profile), | 74 profile_(profile), |
74 weak_factory_(this) { | 75 weak_factory_(this) { |
75 } | 76 } |
76 | 77 |
77 PushMessagingServiceImpl::~PushMessagingServiceImpl() { | 78 PushMessagingServiceImpl::~PushMessagingServiceImpl() { |
78 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver, | 79 // TODO(johnme): If it's possible for this to be destroyed before GCMDriver, |
79 // then we should call RemoveAppHandler. | 80 // then we should call RemoveAppHandler. |
80 } | 81 } |
81 | 82 |
82 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const { | 83 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const { |
83 // TODO(mvanouwerkerk): Finalize and centralize format of Push API app_id. | 84 return PushMessagingApplicationId::Parse(app_id).IsValid(); |
84 return StartsWithASCII(app_id, kAppIdPrefix, true); | |
85 } | 85 } |
86 | 86 |
87 void PushMessagingServiceImpl::ShutdownHandler() { | 87 void PushMessagingServiceImpl::ShutdownHandler() { |
88 // TODO(johnme): Do any necessary cleanup. | 88 // TODO(johnme): Do any necessary cleanup. |
89 } | 89 } |
90 | 90 |
91 void PushMessagingServiceImpl::OnMessage( | 91 void PushMessagingServiceImpl::OnMessage( |
92 const std::string& app_id, | 92 const std::string& app_id, |
93 const GCMClient::IncomingMessage& message) { | 93 const GCMClient::IncomingMessage& message) { |
94 // The Push API only exposes a single string of data in the push event fired | 94 // The Push API only exposes a single string of data in the push event fired |
95 // on the Service Worker. When developers send messages using GCM to the Push | 95 // on the Service Worker. When developers send messages using GCM to the Push |
96 // API, they must pass a single key-value pair, where the key is "data" and | 96 // API, they must pass a single key-value pair, where the key is "data" and |
97 // the value is the string they want to be passed to their Service Worker. | 97 // the value is the string they want to be passed to their Service Worker. |
98 // For example, they could send the following JSON using the HTTPS GCM API: | 98 // For example, they could send the following JSON using the HTTPS GCM API: |
99 // { | 99 // { |
100 // "registration_ids": ["FOO", "BAR"], | 100 // "registration_ids": ["FOO", "BAR"], |
101 // "data": { | 101 // "data": { |
102 // "data": "BAZ", | 102 // "data": "BAZ", |
103 // }, | 103 // }, |
104 // "delay_while_idle": true, | 104 // "delay_while_idle": true, |
105 // } | 105 // } |
106 // TODO(johnme): Make sure this is clearly documented for developers. | 106 // TODO(johnme): Make sure this is clearly documented for developers. |
| 107 PushMessagingApplicationId application_id = |
| 108 PushMessagingApplicationId::Parse(app_id); |
| 109 DCHECK(application_id.IsValid()); |
107 GCMClient::MessageData::const_iterator it = message.data.find("data"); | 110 GCMClient::MessageData::const_iterator it = message.data.find("data"); |
108 if (it != message.data.end()) { | 111 if (application_id.IsValid() && it != message.data.end()) { |
109 const std::string& data ALLOW_UNUSED = it->second; | 112 const std::string& data ALLOW_UNUSED = it->second; |
110 // TODO(mvanouwerkerk): Fire push event with data on the Service Worker | 113 // TODO(mvanouwerkerk): Fire push event with data on the Service Worker |
111 // corresponding to app_id (and remove ALLOW_UNUSED above). | 114 // corresponding to app_id (and remove ALLOW_UNUSED above). |
112 } else { | 115 } else { |
113 // Drop the message, as it is invalid. | 116 // Drop the message, as it is invalid. |
114 // TODO(mvanouwerkerk): Show a warning in the developer console of the | 117 // TODO(mvanouwerkerk): Show a warning in the developer console of the |
115 // Service Worker corresponding to app_id. | 118 // Service Worker corresponding to app_id. |
116 // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page) | 119 // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page) |
117 // to know when bad things happen. | 120 // to know when bad things happen. |
118 } | 121 } |
119 } | 122 } |
120 | 123 |
121 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { | 124 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) { |
122 // TODO(mvanouwerkerk): Fire push error event on the Service Worker | 125 // TODO(mvanouwerkerk): Fire push error event on the Service Worker |
123 // corresponding to app_id. | 126 // corresponding to app_id. |
124 } | 127 } |
125 | 128 |
126 void PushMessagingServiceImpl::OnSendError( | 129 void PushMessagingServiceImpl::OnSendError( |
127 const std::string& app_id, | 130 const std::string& app_id, |
128 const GCMClient::SendErrorDetails& send_error_details) { | 131 const GCMClient::SendErrorDetails& send_error_details) { |
129 NOTREACHED() << "The Push API shouldn't have sent messages upstream"; | 132 NOTREACHED() << "The Push API shouldn't have sent messages upstream"; |
130 } | 133 } |
131 | 134 |
132 void PushMessagingServiceImpl::Register( | 135 void PushMessagingServiceImpl::Register( |
133 const std::string& app_id, | 136 const GURL& origin, |
| 137 int64 service_worker_registration_id, |
134 const std::string& sender_id, | 138 const std::string& sender_id, |
135 int renderer_id, | 139 int renderer_id, |
136 int render_frame_id, | 140 int render_frame_id, |
137 bool user_gesture, | 141 bool user_gesture, |
138 const content::PushMessagingService::RegisterCallback& callback) { | 142 const content::PushMessagingService::RegisterCallback& callback) { |
139 if (!gcm_profile_service_->driver()) { | 143 if (!gcm_profile_service_->driver()) { |
140 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?"; | 144 NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?"; |
141 } | 145 } |
142 | 146 |
| 147 PushMessagingApplicationId application_id = |
| 148 PushMessagingApplicationId(origin, service_worker_registration_id); |
| 149 DCHECK(application_id.IsValid()); |
| 150 |
143 if (profile_->GetPrefs()->GetInteger( | 151 if (profile_->GetPrefs()->GetInteger( |
144 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) { | 152 prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) { |
145 RegisterEnd( | 153 RegisterEnd( |
146 app_id, | |
147 callback, | 154 callback, |
148 std::string(), | 155 std::string(), |
149 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_LIMIT_REACHED); | 156 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_LIMIT_REACHED); |
150 return; | 157 return; |
151 } | 158 } |
152 | 159 |
153 // If this is registering for the first time then the driver does not have | 160 // If this is registering for the first time then the driver does not have |
154 // this as an app handler and registration would fail. | 161 // this as an app handler and registration would fail. |
155 if (gcm_profile_service_->driver()->GetAppHandler(kAppIdPrefix) != this) | 162 if (gcm_profile_service_->driver()->GetAppHandler( |
156 gcm_profile_service_->driver()->AddAppHandler(kAppIdPrefix, this); | 163 kPushMessagingApplicationIdPrefix) != this) |
| 164 gcm_profile_service_->driver()->AddAppHandler( |
| 165 kPushMessagingApplicationIdPrefix, this); |
157 | 166 |
158 content::RenderFrameHost* render_frame_host = | 167 content::RenderFrameHost* render_frame_host = |
159 content::RenderFrameHost::FromID(renderer_id, render_frame_id); | 168 content::RenderFrameHost::FromID(renderer_id, render_frame_id); |
160 | 169 |
161 // The frame doesn't exist any more, or we received a bad frame id. | 170 // The frame doesn't exist any more, or we received a bad frame id. |
162 if (!render_frame_host) | 171 if (!render_frame_host) |
163 return; | 172 return; |
164 | 173 |
165 content::WebContents* web_contents = | 174 content::WebContents* web_contents = |
166 content::WebContents::FromRenderFrameHost(render_frame_host); | 175 content::WebContents::FromRenderFrameHost(render_frame_host); |
167 | 176 |
168 // The page doesn't exist any more or we got a bad render frame host. | 177 // The page doesn't exist any more or we got a bad render frame host. |
169 if (!web_contents) | 178 if (!web_contents) |
170 return; | 179 return; |
171 | 180 |
172 // TODO(miguelg) need to send this over IPC when bubble support is | 181 // TODO(miguelg) need to send this over IPC when bubble support is |
173 // implemented. | 182 // implemented. |
174 int bridge_id = -1; | 183 int bridge_id = -1; |
175 | 184 |
176 const PermissionRequestID id( | 185 const PermissionRequestID id( |
177 renderer_id, web_contents->GetRoutingID(), bridge_id, GURL()); | 186 renderer_id, web_contents->GetRoutingID(), bridge_id, GURL()); |
178 | 187 |
179 GURL embedder = web_contents->GetLastCommittedURL(); | 188 GURL embedder = web_contents->GetLastCommittedURL(); |
180 gcm::PushMessagingPermissionContext* permission_context = | 189 gcm::PushMessagingPermissionContext* permission_context = |
181 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_); | 190 gcm::PushMessagingPermissionContextFactory::GetForProfile(profile_); |
182 | 191 |
183 if (permission_context == NULL) { | 192 if (permission_context == NULL) { |
184 RegisterEnd( | 193 RegisterEnd( |
185 app_id, | |
186 callback, | 194 callback, |
187 std::string(), | 195 std::string(), |
188 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED); | 196 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED); |
189 return; | 197 return; |
190 } | 198 } |
191 | 199 |
192 permission_context->RequestPermission( | 200 permission_context->RequestPermission( |
193 web_contents, | 201 web_contents, |
194 id, | 202 id, |
195 embedder, | 203 embedder, |
196 user_gesture, | 204 user_gesture, |
197 base::Bind(&PushMessagingServiceImpl::DidRequestPermission, | 205 base::Bind(&PushMessagingServiceImpl::DidRequestPermission, |
198 weak_factory_.GetWeakPtr(), | 206 weak_factory_.GetWeakPtr(), |
| 207 application_id, |
199 sender_id, | 208 sender_id, |
200 app_id, | |
201 callback)); | 209 callback)); |
202 } | 210 } |
203 | 211 |
204 void PushMessagingServiceImpl::RegisterEnd( | 212 void PushMessagingServiceImpl::RegisterEnd( |
205 const std::string& app_id, | |
206 const content::PushMessagingService::RegisterCallback& callback, | 213 const content::PushMessagingService::RegisterCallback& callback, |
207 const std::string& registration_id, | 214 const std::string& registration_id, |
208 content::PushMessagingStatus status) { | 215 content::PushMessagingStatus status) { |
209 GURL endpoint = GURL("https://android.googleapis.com/gcm/send"); | 216 GURL endpoint = GURL("https://android.googleapis.com/gcm/send"); |
210 callback.Run(endpoint, registration_id, status); | 217 callback.Run(endpoint, registration_id, status); |
211 if (status == content::PUSH_MESSAGING_STATUS_OK) { | 218 if (status == content::PUSH_MESSAGING_STATUS_OK) { |
212 // TODO(johnme): Make sure the pref doesn't get out of sync after crashes. | 219 // TODO(johnme): Make sure the pref doesn't get out of sync after crashes. |
213 int registration_count = profile_->GetPrefs()->GetInteger( | 220 int registration_count = profile_->GetPrefs()->GetInteger( |
214 prefs::kPushMessagingRegistrationCount); | 221 prefs::kPushMessagingRegistrationCount); |
215 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount, | 222 profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount, |
216 registration_count + 1); | 223 registration_count + 1); |
217 } | 224 } |
218 } | 225 } |
219 | 226 |
220 void PushMessagingServiceImpl::DidRegister( | 227 void PushMessagingServiceImpl::DidRegister( |
221 const std::string& app_id, | |
222 const content::PushMessagingService::RegisterCallback& callback, | 228 const content::PushMessagingService::RegisterCallback& callback, |
223 const std::string& registration_id, | 229 const std::string& registration_id, |
224 GCMClient::Result result) { | 230 GCMClient::Result result) { |
225 content::PushMessagingStatus status = | 231 content::PushMessagingStatus status = |
226 result == GCMClient::SUCCESS | 232 result == GCMClient::SUCCESS |
227 ? content::PUSH_MESSAGING_STATUS_OK | 233 ? content::PUSH_MESSAGING_STATUS_OK |
228 : content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_SERVICE_ERROR; | 234 : content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_SERVICE_ERROR; |
229 RegisterEnd(app_id, callback, registration_id, status); | 235 RegisterEnd(callback, registration_id, status); |
230 } | 236 } |
231 | 237 |
232 void PushMessagingServiceImpl::DidRequestPermission( | 238 void PushMessagingServiceImpl::DidRequestPermission( |
| 239 const PushMessagingApplicationId& application_id, |
233 const std::string& sender_id, | 240 const std::string& sender_id, |
234 const std::string& app_id, | |
235 const content::PushMessagingService::RegisterCallback& register_callback, | 241 const content::PushMessagingService::RegisterCallback& register_callback, |
236 bool allow) { | 242 bool allow) { |
237 if (!allow) { | 243 if (!allow) { |
238 RegisterEnd( | 244 RegisterEnd( |
239 app_id, | |
240 register_callback, | 245 register_callback, |
241 std::string(), | 246 std::string(), |
242 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED); | 247 content::PUSH_MESSAGING_STATUS_REGISTRATION_FAILED_PERMISSION_DENIED); |
243 return; | 248 return; |
244 } | 249 } |
245 | 250 |
246 // The GCMDriver could be NULL if GCMProfileService has been shut down. | 251 // The GCMDriver could be NULL if GCMProfileService has been shut down. |
247 if (!gcm_profile_service_->driver()) | 252 if (!gcm_profile_service_->driver()) |
248 return; | 253 return; |
249 | 254 |
250 std::vector<std::string> sender_ids(1, sender_id); | 255 std::vector<std::string> sender_ids(1, sender_id); |
251 | 256 |
252 gcm_profile_service_->driver()->Register( | 257 gcm_profile_service_->driver()->Register( |
253 app_id, | 258 application_id.ToString(), |
254 sender_ids, | 259 sender_ids, |
255 base::Bind(&PushMessagingServiceImpl::DidRegister, | 260 base::Bind(&PushMessagingServiceImpl::DidRegister, |
256 weak_factory_.GetWeakPtr(), | 261 weak_factory_.GetWeakPtr(), |
257 app_id, | |
258 register_callback)); | 262 register_callback)); |
259 } | 263 } |
260 | 264 |
261 // TODO(johnme): Unregister should decrement the pref, and call | 265 // TODO(johnme): Unregister should decrement the pref, and call |
262 // RemoveAppHandler if the count drops to zero. | 266 // RemoveAppHandler if the count drops to zero. |
263 | 267 |
264 } // namespace gcm | 268 } // namespace gcm |
OLD | NEW |