OLD | NEW |
| (Empty) |
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 "content/browser/push_messaging/push_messaging_message_filter.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/command_line.h" | |
13 #include "base/logging.h" | |
14 #include "base/macros.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/metrics/histogram_macros.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "content/browser/renderer_host/render_process_host_impl.h" | |
19 #include "content/browser/service_worker/service_worker_context_core.h" | |
20 #include "content/browser/service_worker/service_worker_context_wrapper.h" | |
21 #include "content/browser/service_worker/service_worker_storage.h" | |
22 #include "content/common/push_messaging_messages.h" | |
23 #include "content/public/browser/browser_context.h" | |
24 #include "content/public/browser/browser_thread.h" | |
25 #include "content/public/browser/permission_manager.h" | |
26 #include "content/public/browser/permission_type.h" | |
27 #include "content/public/browser/push_messaging_service.h" | |
28 #include "content/public/browser/render_frame_host.h" | |
29 #include "content/public/browser/web_contents.h" | |
30 #include "content/public/common/child_process_host.h" | |
31 #include "content/public/common/console_message_level.h" | |
32 #include "content/public/common/content_switches.h" | |
33 #include "content/public/common/push_messaging_status.h" | |
34 #include "third_party/WebKit/public/platform/modules/push_messaging/WebPushPermi
ssionStatus.h" | |
35 | |
36 namespace content { | |
37 | |
38 // Service Worker database keys. If a registration ID is stored, the stored | |
39 // sender ID must be the one used to register. Unfortunately, this isn't always | |
40 // true of pre-InstanceID registrations previously stored in the database, but | |
41 // fortunately it's less important for their sender ID to be accurate. | |
42 const char kPushSenderIdServiceWorkerKey[] = "push_sender_id"; | |
43 const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id"; | |
44 | |
45 namespace { | |
46 | |
47 // Chrome currently does not support the Push API in incognito. | |
48 const char kIncognitoPushUnsupportedMessage[] = | |
49 "Chrome currently does not support the Push API in incognito mode " | |
50 "(https://crbug.com/401439). There is deliberately no way to " | |
51 "feature-detect this, since incognito mode needs to be undetectable by " | |
52 "websites."; | |
53 | |
54 // These UMA methods are only called from IO thread, but it would be acceptable | |
55 // (even though slightly racy) to call them from UI thread as well, see | |
56 // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/FNzZRJtN2aw/Aw0CW
AXJJ1kJ | |
57 void RecordRegistrationStatus(PushRegistrationStatus status) { | |
58 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
59 UMA_HISTOGRAM_ENUMERATION("PushMessaging.RegistrationStatus", status, | |
60 PUSH_REGISTRATION_STATUS_LAST + 1); | |
61 } | |
62 | |
63 void RecordUnregistrationStatus(PushUnregistrationStatus status) { | |
64 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
65 UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationStatus", status, | |
66 PUSH_UNREGISTRATION_STATUS_LAST + 1); | |
67 } | |
68 | |
69 void RecordGetRegistrationStatus(PushGetRegistrationStatus status) { | |
70 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
71 UMA_HISTOGRAM_ENUMERATION("PushMessaging.GetRegistrationStatus", status, | |
72 PUSH_GETREGISTRATION_STATUS_LAST + 1); | |
73 } | |
74 | |
75 // Curries the |success| and |p256dh| parameters over to |callback| and | |
76 // posts a task to invoke |callback| on the IO thread. | |
77 void ForwardEncryptionInfoToIOThreadProxy( | |
78 const PushMessagingService::EncryptionInfoCallback& callback, | |
79 bool success, | |
80 const std::vector<uint8_t>& p256dh, | |
81 const std::vector<uint8_t>& auth) { | |
82 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
83 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
84 base::Bind(callback, success, p256dh, auth)); | |
85 } | |
86 | |
87 // Returns whether |sender_info| contains a valid application server key, that | |
88 // is, a NIST P-256 public key in uncompressed format. | |
89 bool IsApplicationServerKey(const std::string& sender_info) { | |
90 return sender_info.size() == 65 && sender_info[0] == 0x04; | |
91 } | |
92 | |
93 // Returns sender_info if non-empty, otherwise checks if stored_sender_id | |
94 // may be used as a fallback and if so, returns stored_sender_id instead. | |
95 // | |
96 // This is in order to support the legacy way of subscribing from a service | |
97 // worker (first subscribe from the document using a gcm_sender_id set in the | |
98 // manifest, and then subscribe from the service worker with no key). | |
99 // | |
100 // An empty string will be returned if sender_info is empty and the fallback | |
101 // is not a numeric gcm sender id. | |
102 std::string FixSenderInfo(const std::string& sender_info, | |
103 const std::string& stored_sender_id) { | |
104 if (!sender_info.empty()) | |
105 return sender_info; | |
106 if (base::ContainsOnlyChars(stored_sender_id, "0123456789")) | |
107 return stored_sender_id; | |
108 return std::string(); | |
109 } | |
110 | |
111 } // namespace | |
112 | |
113 struct PushMessagingMessageFilter::RegisterData { | |
114 RegisterData(); | |
115 RegisterData(const RegisterData& other) = default; | |
116 bool FromDocument() const; | |
117 int request_id; | |
118 GURL requesting_origin; | |
119 int64_t service_worker_registration_id; | |
120 PushSubscriptionOptions options; | |
121 // The following member should only be read if FromDocument() is true. | |
122 int render_frame_id; | |
123 }; | |
124 | |
125 // Inner core of this message filter which lives on the UI thread. | |
126 class PushMessagingMessageFilter::Core { | |
127 public: | |
128 Core(const base::WeakPtr<PushMessagingMessageFilter>& io_parent, | |
129 int render_process_id); | |
130 | |
131 // Public Register methods on UI thread -------------------------------------- | |
132 | |
133 // Called via PostTask from IO thread. | |
134 void RegisterOnUI(const RegisterData& data); | |
135 | |
136 // Public Unregister methods on UI thread ------------------------------------ | |
137 | |
138 // Called via PostTask from IO thread. | |
139 void UnregisterFromService(int request_id, | |
140 int64_t service_worker_registration_id, | |
141 const GURL& requesting_origin, | |
142 const std::string& sender_id); | |
143 | |
144 // Public GetPermission methods on UI thread --------------------------------- | |
145 | |
146 // Called via PostTask from IO thread. | |
147 void GetPermissionStatusOnUI(const GURL& requesting_origin, | |
148 bool user_visible, | |
149 int request_id); | |
150 | |
151 // Public helper methods on UI thread ---------------------------------------- | |
152 | |
153 // Called via PostTask from IO thread. The |io_thread_callback| callback | |
154 // will be invoked on the IO thread. | |
155 void GetEncryptionInfoOnUI( | |
156 const GURL& origin, | |
157 int64_t service_worker_registration_id, | |
158 const std::string& sender_id, | |
159 const PushMessagingService::EncryptionInfoCallback& io_thread_callback); | |
160 | |
161 // Called (directly) from both the UI and IO threads. | |
162 bool is_incognito() const { return is_incognito_; } | |
163 | |
164 // Returns a push messaging service. May return null. | |
165 PushMessagingService* service(); | |
166 | |
167 private: | |
168 friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>; | |
169 friend class base::DeleteHelper<Core>; | |
170 | |
171 ~Core(); | |
172 | |
173 // Private Register methods on UI thread ------------------------------------- | |
174 | |
175 void DidRequestPermissionInIncognito(const RegisterData& data, | |
176 blink::mojom::PermissionStatus status); | |
177 | |
178 void DidRegister(const RegisterData& data, | |
179 const std::string& push_registration_id, | |
180 const std::vector<uint8_t>& p256dh, | |
181 const std::vector<uint8_t>& auth, | |
182 PushRegistrationStatus status); | |
183 | |
184 // Private Unregister methods on UI thread ----------------------------------- | |
185 | |
186 void DidUnregisterFromService(int request_id, | |
187 int64_t service_worker_registration_id, | |
188 PushUnregistrationStatus unregistration_status); | |
189 | |
190 // Private helper methods on UI thread --------------------------------------- | |
191 | |
192 void Send(IPC::Message* message); | |
193 | |
194 // Outer part of this message filter which lives on the IO thread. | |
195 base::WeakPtr<PushMessagingMessageFilter> io_parent_; | |
196 | |
197 int render_process_id_; | |
198 | |
199 bool is_incognito_; | |
200 | |
201 base::WeakPtrFactory<Core> weak_factory_ui_to_ui_; | |
202 | |
203 DISALLOW_COPY_AND_ASSIGN(Core); | |
204 }; | |
205 | |
206 PushMessagingMessageFilter::RegisterData::RegisterData() | |
207 : request_id(0), | |
208 service_worker_registration_id(0), | |
209 render_frame_id(ChildProcessHost::kInvalidUniqueID) {} | |
210 | |
211 bool PushMessagingMessageFilter::RegisterData::FromDocument() const { | |
212 return render_frame_id != ChildProcessHost::kInvalidUniqueID; | |
213 } | |
214 | |
215 PushMessagingMessageFilter::Core::Core( | |
216 const base::WeakPtr<PushMessagingMessageFilter>& io_parent, | |
217 int render_process_id) | |
218 : io_parent_(io_parent), | |
219 render_process_id_(render_process_id), | |
220 weak_factory_ui_to_ui_(this) { | |
221 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
222 RenderProcessHost* process_host = | |
223 RenderProcessHost::FromID(render_process_id_); // Can't be null yet. | |
224 is_incognito_ = process_host->GetBrowserContext()->IsOffTheRecord(); | |
225 } | |
226 | |
227 PushMessagingMessageFilter::Core::~Core() {} | |
228 | |
229 PushMessagingMessageFilter::PushMessagingMessageFilter( | |
230 int render_process_id, | |
231 ServiceWorkerContextWrapper* service_worker_context) | |
232 : BrowserMessageFilter(PushMessagingMsgStart), | |
233 service_worker_context_(service_worker_context), | |
234 weak_factory_io_to_io_(this) { | |
235 // Although this class is used only on the IO thread, it is constructed on UI. | |
236 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
237 // Normally, it would be unsafe to obtain a weak pointer from the UI thread, | |
238 // but it's ok in the constructor since we can't be destroyed before our | |
239 // constructor finishes. | |
240 ui_core_.reset( | |
241 new Core(weak_factory_io_to_io_.GetWeakPtr(), render_process_id)); | |
242 | |
243 PushMessagingService* service = ui_core_->service(); | |
244 service_available_ = !!service; | |
245 | |
246 if (service_available_) { | |
247 default_endpoint_ = service->GetEndpoint(false /* standard_protocol */); | |
248 web_push_protocol_endpoint_ = | |
249 service->GetEndpoint(true /* standard_protocol */); | |
250 } | |
251 } | |
252 | |
253 PushMessagingMessageFilter::~PushMessagingMessageFilter() {} | |
254 | |
255 void PushMessagingMessageFilter::OnDestruct() const { | |
256 BrowserThread::DeleteOnIOThread::Destruct(this); | |
257 } | |
258 | |
259 bool PushMessagingMessageFilter::OnMessageReceived( | |
260 const IPC::Message& message) { | |
261 bool handled = true; | |
262 IPC_BEGIN_MESSAGE_MAP(PushMessagingMessageFilter, message) | |
263 IPC_MESSAGE_HANDLER(PushMessagingHostMsg_Subscribe, OnSubscribe) | |
264 IPC_MESSAGE_HANDLER(PushMessagingHostMsg_Unsubscribe, OnUnsubscribe) | |
265 IPC_MESSAGE_HANDLER(PushMessagingHostMsg_GetSubscription, OnGetSubscription) | |
266 IPC_MESSAGE_HANDLER(PushMessagingHostMsg_GetPermissionStatus, | |
267 OnGetPermissionStatus) | |
268 IPC_MESSAGE_UNHANDLED(handled = false) | |
269 IPC_END_MESSAGE_MAP() | |
270 return handled; | |
271 } | |
272 | |
273 // Subscribe methods on both IO and UI threads, merged in order of use from | |
274 // PushMessagingMessageFilter and Core. | |
275 // ----------------------------------------------------------------------------- | |
276 | |
277 void PushMessagingMessageFilter::OnSubscribe( | |
278 int render_frame_id, | |
279 int request_id, | |
280 int64_t service_worker_registration_id, | |
281 const PushSubscriptionOptions& options) { | |
282 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
283 // TODO(mvanouwerkerk): Validate arguments? | |
284 RegisterData data; | |
285 | |
286 // Will be ChildProcessHost::kInvalidUniqueID in requests from Service Worker. | |
287 data.render_frame_id = render_frame_id; | |
288 | |
289 data.request_id = request_id; | |
290 data.service_worker_registration_id = service_worker_registration_id; | |
291 data.options = options; | |
292 | |
293 ServiceWorkerRegistration* service_worker_registration = | |
294 service_worker_context_->GetLiveRegistration( | |
295 data.service_worker_registration_id); | |
296 if (!service_worker_registration || | |
297 !service_worker_registration->active_version()) { | |
298 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER); | |
299 return; | |
300 } | |
301 data.requesting_origin = service_worker_registration->pattern().GetOrigin(); | |
302 | |
303 DCHECK(!(data.options.sender_info.empty() && data.FromDocument())); | |
304 | |
305 service_worker_context_->GetRegistrationUserData( | |
306 data.service_worker_registration_id, | |
307 {kPushRegistrationIdServiceWorkerKey, kPushSenderIdServiceWorkerKey}, | |
308 base::Bind(&PushMessagingMessageFilter::DidCheckForExistingRegistration, | |
309 weak_factory_io_to_io_.GetWeakPtr(), data)); | |
310 } | |
311 | |
312 void PushMessagingMessageFilter::DidCheckForExistingRegistration( | |
313 const RegisterData& data, | |
314 const std::vector<std::string>& push_registration_id_and_sender_id, | |
315 ServiceWorkerStatusCode service_worker_status) { | |
316 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
317 if (service_worker_status == SERVICE_WORKER_OK) { | |
318 DCHECK_EQ(2u, push_registration_id_and_sender_id.size()); | |
319 const auto& push_registration_id = push_registration_id_and_sender_id[0]; | |
320 const auto& stored_sender_id = push_registration_id_and_sender_id[1]; | |
321 std::string fixed_sender_id = | |
322 FixSenderInfo(data.options.sender_info, stored_sender_id); | |
323 if (fixed_sender_id.empty()) { | |
324 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_NO_SENDER_ID); | |
325 return; | |
326 } | |
327 if (fixed_sender_id != stored_sender_id) { | |
328 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_SENDER_ID_MISMATCH); | |
329 return; | |
330 } | |
331 auto callback = base::Bind( | |
332 &PushMessagingMessageFilter::DidGetEncryptionKeys, | |
333 weak_factory_io_to_io_.GetWeakPtr(), data, push_registration_id); | |
334 BrowserThread::PostTask( | |
335 BrowserThread::UI, FROM_HERE, | |
336 base::Bind(&Core::GetEncryptionInfoOnUI, | |
337 base::Unretained(ui_core_.get()), data.requesting_origin, | |
338 data.service_worker_registration_id, fixed_sender_id, | |
339 callback)); | |
340 return; | |
341 } | |
342 // TODO(johnme): The spec allows the register algorithm to reject with an | |
343 // AbortError when accessing storage fails. Perhaps we should do that if | |
344 // service_worker_status != SERVICE_WORKER_ERROR_NOT_FOUND instead of | |
345 // attempting to do a fresh registration? | |
346 // https://w3c.github.io/push-api/#widl-PushRegistrationManager-register-Promi
se-PushRegistration | |
347 if (!data.options.sender_info.empty()) { | |
348 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
349 base::Bind(&Core::RegisterOnUI, | |
350 base::Unretained(ui_core_.get()), data)); | |
351 } else { | |
352 // There is no existing registration and the sender_info passed in was | |
353 // empty, but perhaps there is a stored sender id we can use. | |
354 service_worker_context_->GetRegistrationUserData( | |
355 data.service_worker_registration_id, {kPushSenderIdServiceWorkerKey}, | |
356 base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage, | |
357 weak_factory_io_to_io_.GetWeakPtr(), data)); | |
358 } | |
359 } | |
360 | |
361 void PushMessagingMessageFilter::DidGetEncryptionKeys( | |
362 const RegisterData& data, | |
363 const std::string& push_registration_id, | |
364 bool success, | |
365 const std::vector<uint8_t>& p256dh, | |
366 const std::vector<uint8_t>& auth) { | |
367 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
368 if (!success) { | |
369 SendSubscriptionError( | |
370 data, PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE); | |
371 return; | |
372 } | |
373 | |
374 SendSubscriptionSuccess(data, PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE, | |
375 push_registration_id, p256dh, auth); | |
376 } | |
377 | |
378 void PushMessagingMessageFilter::DidGetSenderIdFromStorage( | |
379 const RegisterData& data, | |
380 const std::vector<std::string>& stored_sender_id, | |
381 ServiceWorkerStatusCode service_worker_status) { | |
382 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
383 if (service_worker_status != SERVICE_WORKER_OK) { | |
384 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_NO_SENDER_ID); | |
385 return; | |
386 } | |
387 DCHECK_EQ(1u, stored_sender_id.size()); | |
388 // We should only be here because no sender info was supplied to subscribe(). | |
389 DCHECK(data.options.sender_info.empty()); | |
390 std::string fixed_sender_id = | |
391 FixSenderInfo(data.options.sender_info, stored_sender_id[0]); | |
392 if (fixed_sender_id.empty()) { | |
393 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_NO_SENDER_ID); | |
394 return; | |
395 } | |
396 RegisterData mutated_data = data; | |
397 mutated_data.options.sender_info = fixed_sender_id; | |
398 BrowserThread::PostTask( | |
399 BrowserThread::UI, FROM_HERE, | |
400 base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()), | |
401 mutated_data)); | |
402 } | |
403 | |
404 void PushMessagingMessageFilter::Core::RegisterOnUI( | |
405 const PushMessagingMessageFilter::RegisterData& data) { | |
406 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
407 PushMessagingService* push_service = service(); | |
408 if (!push_service) { | |
409 if (!is_incognito()) { | |
410 // This might happen if InstanceIDProfileService::IsInstanceIDEnabled | |
411 // returns false because the Instance ID kill switch was enabled. | |
412 // TODO(johnme): Might be better not to expose the API in this case. | |
413 BrowserThread::PostTask( | |
414 BrowserThread::IO, FROM_HERE, | |
415 base::Bind(&PushMessagingMessageFilter::SendSubscriptionError, | |
416 io_parent_, | |
417 data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE)); | |
418 } else { | |
419 // Prevent websites from detecting incognito mode, by emulating what would | |
420 // have happened if we had a PushMessagingService available. | |
421 if (!data.FromDocument() || !data.options.user_visible_only) { | |
422 // Throw a permission denied error under the same circumstances. | |
423 BrowserThread::PostTask( | |
424 BrowserThread::IO, FROM_HERE, | |
425 base::Bind(&PushMessagingMessageFilter::SendSubscriptionError, | |
426 io_parent_, data, | |
427 PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED)); | |
428 } else { | |
429 RenderFrameHost* render_frame_host = | |
430 RenderFrameHost::FromID(render_process_id_, data.render_frame_id); | |
431 WebContents* web_contents = | |
432 WebContents::FromRenderFrameHost(render_frame_host); | |
433 if (web_contents) { | |
434 web_contents->GetMainFrame()->AddMessageToConsole( | |
435 CONSOLE_MESSAGE_LEVEL_ERROR, kIncognitoPushUnsupportedMessage); | |
436 // Request push messaging permission (which will fail, since | |
437 // notifications aren't supported in incognito), so the website can't | |
438 // detect whether incognito is active. | |
439 web_contents->GetBrowserContext() | |
440 ->GetPermissionManager() | |
441 ->RequestPermission( | |
442 PermissionType::PUSH_MESSAGING, render_frame_host, | |
443 data.requesting_origin, false /* user_gesture */, | |
444 base::Bind(&PushMessagingMessageFilter::Core:: | |
445 DidRequestPermissionInIncognito, | |
446 weak_factory_ui_to_ui_.GetWeakPtr(), data)); | |
447 } | |
448 } | |
449 } | |
450 return; | |
451 } | |
452 | |
453 if (data.FromDocument()) { | |
454 push_service->SubscribeFromDocument( | |
455 data.requesting_origin, data.service_worker_registration_id, | |
456 render_process_id_, data.render_frame_id, data.options, | |
457 base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(), | |
458 data)); | |
459 } else { | |
460 push_service->SubscribeFromWorker( | |
461 data.requesting_origin, data.service_worker_registration_id, | |
462 data.options, base::Bind(&Core::DidRegister, | |
463 weak_factory_ui_to_ui_.GetWeakPtr(), data)); | |
464 } | |
465 } | |
466 | |
467 void PushMessagingMessageFilter::Core::DidRequestPermissionInIncognito( | |
468 const RegisterData& data, | |
469 blink::mojom::PermissionStatus status) { | |
470 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
471 // Notification permission should always be denied in incognito. | |
472 DCHECK_EQ(blink::mojom::PermissionStatus::DENIED, status); | |
473 BrowserThread::PostTask( | |
474 BrowserThread::IO, FROM_HERE, | |
475 base::Bind(&PushMessagingMessageFilter::SendSubscriptionError, io_parent_, | |
476 data, PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED)); | |
477 } | |
478 | |
479 void PushMessagingMessageFilter::Core::DidRegister( | |
480 const RegisterData& data, | |
481 const std::string& push_registration_id, | |
482 const std::vector<uint8_t>& p256dh, | |
483 const std::vector<uint8_t>& auth, | |
484 PushRegistrationStatus status) { | |
485 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
486 if (status == PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE) { | |
487 BrowserThread::PostTask( | |
488 BrowserThread::IO, FROM_HERE, | |
489 base::Bind(&PushMessagingMessageFilter::PersistRegistrationOnIO, | |
490 io_parent_, data, push_registration_id, p256dh, auth)); | |
491 } else { | |
492 BrowserThread::PostTask( | |
493 BrowserThread::IO, FROM_HERE, | |
494 base::Bind(&PushMessagingMessageFilter::SendSubscriptionError, | |
495 io_parent_, data, status)); | |
496 } | |
497 } | |
498 | |
499 void PushMessagingMessageFilter::PersistRegistrationOnIO( | |
500 const RegisterData& data, | |
501 const std::string& push_registration_id, | |
502 const std::vector<uint8_t>& p256dh, | |
503 const std::vector<uint8_t>& auth) { | |
504 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
505 service_worker_context_->StoreRegistrationUserData( | |
506 data.service_worker_registration_id, data.requesting_origin, | |
507 {{kPushRegistrationIdServiceWorkerKey, push_registration_id}, | |
508 {kPushSenderIdServiceWorkerKey, data.options.sender_info}}, | |
509 base::Bind(&PushMessagingMessageFilter::DidPersistRegistrationOnIO, | |
510 weak_factory_io_to_io_.GetWeakPtr(), data, | |
511 push_registration_id, p256dh, auth)); | |
512 } | |
513 | |
514 void PushMessagingMessageFilter::DidPersistRegistrationOnIO( | |
515 const RegisterData& data, | |
516 const std::string& push_registration_id, | |
517 const std::vector<uint8_t>& p256dh, | |
518 const std::vector<uint8_t>& auth, | |
519 ServiceWorkerStatusCode service_worker_status) { | |
520 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
521 if (service_worker_status == SERVICE_WORKER_OK) { | |
522 SendSubscriptionSuccess(data, | |
523 PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE, | |
524 push_registration_id, p256dh, auth); | |
525 } else { | |
526 // TODO(johnme): Unregister, so PushMessagingServiceImpl can decrease count. | |
527 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_STORAGE_ERROR); | |
528 } | |
529 } | |
530 | |
531 void PushMessagingMessageFilter::SendSubscriptionError( | |
532 const RegisterData& data, PushRegistrationStatus status) { | |
533 // Only called from IO thread, but would be safe to call from UI thread. | |
534 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
535 if (data.FromDocument()) { | |
536 Send(new PushMessagingMsg_SubscribeFromDocumentError( | |
537 data.render_frame_id, data.request_id, status)); | |
538 } else { | |
539 Send( | |
540 new PushMessagingMsg_SubscribeFromWorkerError(data.request_id, status)); | |
541 } | |
542 RecordRegistrationStatus(status); | |
543 } | |
544 | |
545 void PushMessagingMessageFilter::SendSubscriptionSuccess( | |
546 const RegisterData& data, | |
547 PushRegistrationStatus status, | |
548 const std::string& push_subscription_id, | |
549 const std::vector<uint8_t>& p256dh, | |
550 const std::vector<uint8_t>& auth) { | |
551 // Only called from IO thread, but would be safe to call from UI thread. | |
552 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
553 if (!service_available_) { | |
554 // This shouldn't be possible in incognito mode, since we've already checked | |
555 // that we have an existing registration. Hence it's ok to throw an error. | |
556 DCHECK(!ui_core_->is_incognito()); | |
557 SendSubscriptionError(data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE); | |
558 return; | |
559 } | |
560 | |
561 const GURL endpoint = CreateEndpoint( | |
562 IsApplicationServerKey(data.options.sender_info), push_subscription_id); | |
563 | |
564 if (data.FromDocument()) { | |
565 Send(new PushMessagingMsg_SubscribeFromDocumentSuccess( | |
566 data.render_frame_id, data.request_id, endpoint, data.options, p256dh, | |
567 auth)); | |
568 } else { | |
569 Send(new PushMessagingMsg_SubscribeFromWorkerSuccess( | |
570 data.request_id, endpoint, data.options, p256dh, auth)); | |
571 } | |
572 RecordRegistrationStatus(status); | |
573 } | |
574 | |
575 // Unsubscribe methods on both IO and UI threads, merged in order of use from | |
576 // PushMessagingMessageFilter and Core. | |
577 // ----------------------------------------------------------------------------- | |
578 | |
579 void PushMessagingMessageFilter::OnUnsubscribe( | |
580 int request_id, int64_t service_worker_registration_id) { | |
581 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
582 ServiceWorkerRegistration* service_worker_registration = | |
583 service_worker_context_->GetLiveRegistration( | |
584 service_worker_registration_id); | |
585 if (!service_worker_registration) { | |
586 DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER); | |
587 return; | |
588 } | |
589 | |
590 service_worker_context_->GetRegistrationUserData( | |
591 service_worker_registration_id, {kPushSenderIdServiceWorkerKey}, | |
592 base::Bind(&PushMessagingMessageFilter::UnsubscribeHavingGottenSenderId, | |
593 weak_factory_io_to_io_.GetWeakPtr(), request_id, | |
594 service_worker_registration_id, | |
595 service_worker_registration->pattern().GetOrigin())); | |
596 } | |
597 | |
598 void PushMessagingMessageFilter::UnsubscribeHavingGottenSenderId( | |
599 int request_id, | |
600 int64_t service_worker_registration_id, | |
601 const GURL& requesting_origin, | |
602 const std::vector<std::string>& sender_ids, | |
603 ServiceWorkerStatusCode service_worker_status) { | |
604 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
605 | |
606 std::string sender_id; | |
607 if (service_worker_status == SERVICE_WORKER_OK) { | |
608 DCHECK_EQ(1u, sender_ids.size()); | |
609 sender_id = sender_ids[0]; | |
610 } | |
611 BrowserThread::PostTask( | |
612 BrowserThread::UI, FROM_HERE, | |
613 base::Bind(&Core::UnregisterFromService, base::Unretained(ui_core_.get()), | |
614 request_id, service_worker_registration_id, requesting_origin, | |
615 sender_id)); | |
616 } | |
617 | |
618 void PushMessagingMessageFilter::Core::UnregisterFromService( | |
619 int request_id, | |
620 int64_t service_worker_registration_id, | |
621 const GURL& requesting_origin, | |
622 const std::string& sender_id) { | |
623 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
624 PushMessagingService* push_service = service(); | |
625 if (!push_service) { | |
626 // This shouldn't be possible in incognito mode, since we've already checked | |
627 // that we have an existing registration. Hence it's ok to throw an error. | |
628 DCHECK(!is_incognito()); | |
629 BrowserThread::PostTask( | |
630 BrowserThread::IO, FROM_HERE, | |
631 base::Bind(&PushMessagingMessageFilter::DidUnregister, io_parent_, | |
632 request_id, | |
633 PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE)); | |
634 return; | |
635 } | |
636 | |
637 push_service->Unsubscribe( | |
638 requesting_origin, service_worker_registration_id, sender_id, | |
639 base::Bind(&Core::DidUnregisterFromService, | |
640 weak_factory_ui_to_ui_.GetWeakPtr(), request_id, | |
641 service_worker_registration_id)); | |
642 } | |
643 | |
644 void PushMessagingMessageFilter::Core::DidUnregisterFromService( | |
645 int request_id, | |
646 int64_t service_worker_registration_id, | |
647 PushUnregistrationStatus unregistration_status) { | |
648 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
649 | |
650 BrowserThread::PostTask( | |
651 BrowserThread::IO, FROM_HERE, | |
652 base::Bind(&PushMessagingMessageFilter::DidUnregister, io_parent_, | |
653 request_id, unregistration_status)); | |
654 } | |
655 | |
656 void PushMessagingMessageFilter::DidUnregister( | |
657 int request_id, | |
658 PushUnregistrationStatus unregistration_status) { | |
659 // Only called from IO thread, but would be safe to call from UI thread. | |
660 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
661 switch (unregistration_status) { | |
662 case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED: | |
663 case PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR: | |
664 case PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR: | |
665 Send(new PushMessagingMsg_UnsubscribeSuccess(request_id, true)); | |
666 break; | |
667 case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED: | |
668 Send(new PushMessagingMsg_UnsubscribeSuccess(request_id, false)); | |
669 break; | |
670 case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER: | |
671 case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE: | |
672 case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR: | |
673 Send(new PushMessagingMsg_UnsubscribeError( | |
674 request_id, blink::WebPushError::ErrorTypeAbort, | |
675 PushUnregistrationStatusToString(unregistration_status))); | |
676 break; | |
677 case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR: | |
678 NOTREACHED(); | |
679 break; | |
680 } | |
681 RecordUnregistrationStatus(unregistration_status); | |
682 } | |
683 | |
684 // GetSubscription methods on both IO and UI threads, merged in order of use | |
685 // from PushMessagingMessageFilter and Core. | |
686 // ----------------------------------------------------------------------------- | |
687 | |
688 void PushMessagingMessageFilter::OnGetSubscription( | |
689 int request_id, | |
690 int64_t service_worker_registration_id) { | |
691 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
692 // TODO(johnme): Validate arguments? | |
693 service_worker_context_->GetRegistrationUserData( | |
694 service_worker_registration_id, | |
695 {kPushRegistrationIdServiceWorkerKey, kPushSenderIdServiceWorkerKey}, | |
696 base::Bind(&PushMessagingMessageFilter::DidGetSubscription, | |
697 weak_factory_io_to_io_.GetWeakPtr(), request_id, | |
698 service_worker_registration_id)); | |
699 } | |
700 | |
701 void PushMessagingMessageFilter::DidGetSubscription( | |
702 int request_id, | |
703 int64_t service_worker_registration_id, | |
704 const std::vector<std::string>& push_subscription_id_and_sender_info, | |
705 ServiceWorkerStatusCode service_worker_status) { | |
706 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
707 PushGetRegistrationStatus get_status = | |
708 PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR; | |
709 switch (service_worker_status) { | |
710 case SERVICE_WORKER_OK: { | |
711 DCHECK_EQ(2u, push_subscription_id_and_sender_info.size()); | |
712 | |
713 if (!service_available_) { | |
714 // Return not found in incognito mode, so websites can't detect it. | |
715 get_status = | |
716 ui_core_->is_incognito() | |
717 ? PUSH_GETREGISTRATION_STATUS_INCOGNITO_REGISTRATION_NOT_FOUND | |
718 : PUSH_GETREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE; | |
719 break; | |
720 } | |
721 | |
722 ServiceWorkerRegistration* registration = | |
723 service_worker_context_->GetLiveRegistration( | |
724 service_worker_registration_id); | |
725 const GURL origin = registration->pattern().GetOrigin(); | |
726 | |
727 const bool uses_standard_protocol = | |
728 IsApplicationServerKey(push_subscription_id_and_sender_info[1]); | |
729 const GURL endpoint = CreateEndpoint( | |
730 uses_standard_protocol, push_subscription_id_and_sender_info[0]); | |
731 | |
732 auto callback = | |
733 base::Bind(&PushMessagingMessageFilter::DidGetSubscriptionKeys, | |
734 weak_factory_io_to_io_.GetWeakPtr(), request_id, endpoint, | |
735 push_subscription_id_and_sender_info[1]); | |
736 | |
737 BrowserThread::PostTask( | |
738 BrowserThread::UI, FROM_HERE, | |
739 base::Bind(&Core::GetEncryptionInfoOnUI, | |
740 base::Unretained(ui_core_.get()), origin, | |
741 service_worker_registration_id, | |
742 push_subscription_id_and_sender_info[1], callback)); | |
743 | |
744 return; | |
745 } | |
746 case SERVICE_WORKER_ERROR_NOT_FOUND: { | |
747 get_status = PUSH_GETREGISTRATION_STATUS_REGISTRATION_NOT_FOUND; | |
748 break; | |
749 } | |
750 case SERVICE_WORKER_ERROR_FAILED: { | |
751 get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR; | |
752 break; | |
753 } | |
754 case SERVICE_WORKER_ERROR_ABORT: | |
755 case SERVICE_WORKER_ERROR_START_WORKER_FAILED: | |
756 case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND: | |
757 case SERVICE_WORKER_ERROR_EXISTS: | |
758 case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED: | |
759 case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED: | |
760 case SERVICE_WORKER_ERROR_IPC_FAILED: | |
761 case SERVICE_WORKER_ERROR_NETWORK: | |
762 case SERVICE_WORKER_ERROR_SECURITY: | |
763 case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED: | |
764 case SERVICE_WORKER_ERROR_STATE: | |
765 case SERVICE_WORKER_ERROR_TIMEOUT: | |
766 case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED: | |
767 case SERVICE_WORKER_ERROR_DISK_CACHE: | |
768 case SERVICE_WORKER_ERROR_REDUNDANT: | |
769 case SERVICE_WORKER_ERROR_DISALLOWED: | |
770 case SERVICE_WORKER_ERROR_MAX_VALUE: { | |
771 NOTREACHED() << "Got unexpected error code: " << service_worker_status | |
772 << " " << ServiceWorkerStatusToString(service_worker_status); | |
773 get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR; | |
774 break; | |
775 } | |
776 } | |
777 Send(new PushMessagingMsg_GetSubscriptionError(request_id, get_status)); | |
778 RecordGetRegistrationStatus(get_status); | |
779 } | |
780 | |
781 void PushMessagingMessageFilter::DidGetSubscriptionKeys( | |
782 int request_id, | |
783 const GURL& endpoint, | |
784 const std::string& sender_info, | |
785 bool success, | |
786 const std::vector<uint8_t>& p256dh, | |
787 const std::vector<uint8_t>& auth) { | |
788 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
789 if (!success) { | |
790 PushGetRegistrationStatus status = | |
791 PUSH_GETREGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE; | |
792 | |
793 Send(new PushMessagingMsg_GetSubscriptionError(request_id, status)); | |
794 | |
795 RecordGetRegistrationStatus(status); | |
796 return; | |
797 } | |
798 | |
799 PushSubscriptionOptions options; | |
800 // Chrome rejects subscription requests with userVisibleOnly false, so it must | |
801 // have been true. TODO(harkness): If Chrome starts accepting silent push | |
802 // subscriptions with userVisibleOnly false, the bool will need to be stored. | |
803 options.user_visible_only = true; | |
804 options.sender_info = sender_info; | |
805 | |
806 Send(new PushMessagingMsg_GetSubscriptionSuccess(request_id, endpoint, | |
807 options, p256dh, auth)); | |
808 | |
809 RecordGetRegistrationStatus(PUSH_GETREGISTRATION_STATUS_SUCCESS); | |
810 } | |
811 | |
812 // GetPermission methods on both IO and UI threads, merged in order of use from | |
813 // PushMessagingMessageFilter and Core. | |
814 // ----------------------------------------------------------------------------- | |
815 | |
816 void PushMessagingMessageFilter::OnGetPermissionStatus( | |
817 int request_id, | |
818 int64_t service_worker_registration_id, | |
819 bool user_visible) { | |
820 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
821 ServiceWorkerRegistration* service_worker_registration = | |
822 service_worker_context_->GetLiveRegistration( | |
823 service_worker_registration_id); | |
824 if (!service_worker_registration) { | |
825 Send(new PushMessagingMsg_GetPermissionStatusError( | |
826 request_id, blink::WebPushError::ErrorTypeAbort)); | |
827 return; | |
828 } | |
829 | |
830 BrowserThread::PostTask( | |
831 BrowserThread::UI, FROM_HERE, | |
832 base::Bind(&Core::GetPermissionStatusOnUI, | |
833 base::Unretained(ui_core_.get()), | |
834 service_worker_registration->pattern().GetOrigin(), | |
835 user_visible, request_id)); | |
836 } | |
837 | |
838 void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI( | |
839 const GURL& requesting_origin, bool user_visible, int request_id) { | |
840 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
841 blink::WebPushPermissionStatus permission_status; | |
842 PushMessagingService* push_service = service(); | |
843 if (push_service) { | |
844 if (!user_visible && !push_service->SupportNonVisibleMessages()) { | |
845 Send(new PushMessagingMsg_GetPermissionStatusError( | |
846 request_id, blink::WebPushError::ErrorTypeNotSupported)); | |
847 return; | |
848 } | |
849 permission_status = | |
850 push_service->GetPermissionStatus(requesting_origin, user_visible); | |
851 } else if (is_incognito()) { | |
852 // Return prompt, so the website can't detect incognito mode. | |
853 permission_status = blink::WebPushPermissionStatusPrompt; | |
854 } else { | |
855 Send(new PushMessagingMsg_GetPermissionStatusError( | |
856 request_id, blink::WebPushError::ErrorTypeAbort)); | |
857 return; | |
858 } | |
859 Send(new PushMessagingMsg_GetPermissionStatusSuccess(request_id, | |
860 permission_status)); | |
861 } | |
862 | |
863 // Helper methods on both IO and UI threads, merged from | |
864 // PushMessagingMessageFilter and Core. | |
865 // ----------------------------------------------------------------------------- | |
866 | |
867 void PushMessagingMessageFilter::Core::GetEncryptionInfoOnUI( | |
868 const GURL& origin, | |
869 int64_t service_worker_registration_id, | |
870 const std::string& sender_id, | |
871 const PushMessagingService::EncryptionInfoCallback& io_thread_callback) { | |
872 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
873 PushMessagingService* push_service = service(); | |
874 if (push_service) { | |
875 push_service->GetEncryptionInfo( | |
876 origin, service_worker_registration_id, sender_id, | |
877 base::Bind(&ForwardEncryptionInfoToIOThreadProxy, io_thread_callback)); | |
878 return; | |
879 } | |
880 | |
881 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
882 base::Bind(io_thread_callback, false /* success */, | |
883 std::vector<uint8_t>() /* p256dh */, | |
884 std::vector<uint8_t>() /* auth */)); | |
885 } | |
886 | |
887 void PushMessagingMessageFilter::Core::Send(IPC::Message* message) { | |
888 BrowserThread::PostTask( | |
889 BrowserThread::IO, FROM_HERE, | |
890 base::Bind(&PushMessagingMessageFilter::SendIPC, io_parent_, | |
891 base::Passed(base::WrapUnique(message)))); | |
892 } | |
893 | |
894 void PushMessagingMessageFilter::SendIPC( | |
895 std::unique_ptr<IPC::Message> message) { | |
896 Send(message.release()); | |
897 } | |
898 | |
899 GURL PushMessagingMessageFilter::CreateEndpoint( | |
900 bool standard_protocol, | |
901 const std::string& subscription_id) const { | |
902 const GURL& base = | |
903 standard_protocol ? web_push_protocol_endpoint_ : default_endpoint_; | |
904 | |
905 return GURL(base.spec() + subscription_id); | |
906 } | |
907 | |
908 PushMessagingService* PushMessagingMessageFilter::Core::service() { | |
909 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
910 RenderProcessHost* process_host = | |
911 RenderProcessHost::FromID(render_process_id_); | |
912 return process_host | |
913 ? process_host->GetBrowserContext()->GetPushMessagingService() | |
914 : nullptr; | |
915 } | |
916 | |
917 } // namespace content | |
OLD | NEW |