blob: 54761ccba226a0f86d1050b5e021d5f300fa01b0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/child/push_messaging/push_provider.h"
#include <memory>
#include <utility>
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/threading/thread_local.h"
#include "content/child/child_thread_impl.h"
#include "content/child/service_worker/web_service_worker_registration_impl.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/push_subscription_options.h"
#include "content/public/common/service_names.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/modules/push_messaging/WebPushSubscription.h"
#include "third_party/WebKit/public/platform/modules/push_messaging/WebPushSubscriptionOptions.h"
namespace content {
namespace {
int CurrentWorkerId() {
return WorkerThread::GetCurrentId();
}
// Returns the id of the given |service_worker_registration|, which
// is only available on the implementation of the interface.
int64_t GetServiceWorkerRegistrationId(
blink::WebServiceWorkerRegistration* service_worker_registration) {
return static_cast<WebServiceWorkerRegistrationImpl*>(
service_worker_registration)
->RegistrationId();
}
} // namespace
blink::WebPushError PushRegistrationStatusToWebPushError(
PushRegistrationStatus status) {
blink::WebPushError::ErrorType error_type =
blink::WebPushError::kErrorTypeAbort;
switch (status) {
case PUSH_REGISTRATION_STATUS_PERMISSION_DENIED:
error_type = blink::WebPushError::kErrorTypeNotAllowed;
break;
case PUSH_REGISTRATION_STATUS_SENDER_ID_MISMATCH:
error_type = blink::WebPushError::kErrorTypeInvalidState;
break;
case PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE:
case PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER:
case PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
case PUSH_REGISTRATION_STATUS_LIMIT_REACHED:
case PUSH_REGISTRATION_STATUS_SERVICE_ERROR:
case PUSH_REGISTRATION_STATUS_NO_SENDER_ID:
case PUSH_REGISTRATION_STATUS_STORAGE_ERROR:
case PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE:
case PUSH_REGISTRATION_STATUS_NETWORK_ERROR:
case PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED:
case PUSH_REGISTRATION_STATUS_PUBLIC_KEY_UNAVAILABLE:
case PUSH_REGISTRATION_STATUS_MANIFEST_EMPTY_OR_MISSING:
case PUSH_REGISTRATION_STATUS_STORAGE_CORRUPT:
case PUSH_REGISTRATION_STATUS_RENDERER_SHUTDOWN:
error_type = blink::WebPushError::kErrorTypeAbort;
break;
}
return blink::WebPushError(
error_type,
blink::WebString::FromUTF8(PushRegistrationStatusToString(status)));
}
static base::LazyInstance<base::ThreadLocalPointer<PushProvider>>::Leaky
g_push_provider_tls = LAZY_INSTANCE_INITIALIZER;
PushProvider::PushProvider(const scoped_refptr<base::SingleThreadTaskRunner>&
main_thread_task_runner) {
DCHECK(main_thread_task_runner);
auto request = mojo::MakeRequest(&push_messaging_manager_);
if (!main_thread_task_runner->BelongsToCurrentThread()) {
main_thread_task_runner->PostTask(
FROM_HERE,
base::Bind(&PushProvider::GetInterface, base::Passed(&request)));
} else {
GetInterface(std::move(request));
}
g_push_provider_tls.Pointer()->Set(this);
}
PushProvider::~PushProvider() {
g_push_provider_tls.Pointer()->Set(nullptr);
}
PushProvider* PushProvider::ThreadSpecificInstance(
const scoped_refptr<base::SingleThreadTaskRunner>&
main_thread_task_runner) {
if (g_push_provider_tls.Pointer()->Get())
return g_push_provider_tls.Pointer()->Get();
PushProvider* provider = new PushProvider(main_thread_task_runner);
if (CurrentWorkerId())
WorkerThread::AddObserver(provider);
return provider;
}
// static
void PushProvider::GetInterface(mojom::PushMessagingRequest request) {
if (ChildThreadImpl::current()) {
ChildThreadImpl::current()->GetConnector()->BindInterface(
mojom::kBrowserServiceName, std::move(request));
}
}
void PushProvider::WillStopCurrentWorkerThread() {
delete this;
}
void PushProvider::Subscribe(
blink::WebServiceWorkerRegistration* service_worker_registration,
const blink::WebPushSubscriptionOptions& options,
bool user_gesture,
std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
DCHECK(service_worker_registration);
DCHECK(callbacks);
int64_t service_worker_registration_id =
GetServiceWorkerRegistrationId(service_worker_registration);
PushSubscriptionOptions content_options;
content_options.user_visible_only = options.user_visible_only;
// Just treat the server key as a string of bytes and pass it to the push
// service.
content_options.sender_info = options.application_server_key.Latin1();
push_messaging_manager_->Subscribe(
ChildProcessHost::kInvalidUniqueID, service_worker_registration_id,
content_options, user_gesture,
// Safe to use base::Unretained because |push_messaging_manager_ |is owned
// by |this|.
base::Bind(&PushProvider::DidSubscribe, base::Unretained(this),
base::Passed(&callbacks)));
}
void PushProvider::DidSubscribe(
std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
content::PushRegistrationStatus status,
const base::Optional<GURL>& endpoint,
const base::Optional<content::PushSubscriptionOptions>& options,
const base::Optional<std::vector<uint8_t>>& p256dh,
const base::Optional<std::vector<uint8_t>>& auth) {
DCHECK(callbacks);
if (status == PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE ||
status == PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE) {
DCHECK(endpoint);
DCHECK(options);
DCHECK(p256dh);
DCHECK(auth);
callbacks->OnSuccess(base::MakeUnique<blink::WebPushSubscription>(
endpoint.value(), options.value().user_visible_only,
blink::WebString::FromLatin1(options.value().sender_info),
p256dh.value(), auth.value()));
} else {
callbacks->OnError(PushRegistrationStatusToWebPushError(status));
}
}
void PushProvider::Unsubscribe(
blink::WebServiceWorkerRegistration* service_worker_registration,
std::unique_ptr<blink::WebPushUnsubscribeCallbacks> callbacks) {
DCHECK(service_worker_registration);
DCHECK(callbacks);
int64_t service_worker_registration_id =
GetServiceWorkerRegistrationId(service_worker_registration);
push_messaging_manager_->Unsubscribe(
service_worker_registration_id,
// Safe to use base::Unretained because |push_messaging_manager_ |is owned
// by |this|.
base::Bind(&PushProvider::DidUnsubscribe, base::Unretained(this),
base::Passed(&callbacks)));
}
void PushProvider::DidUnsubscribe(
std::unique_ptr<blink::WebPushUnsubscribeCallbacks> callbacks,
blink::WebPushError::ErrorType error_type,
bool did_unsubscribe,
const base::Optional<std::string>& error_message) {
DCHECK(callbacks);
// ErrorTypeNone indicates success.
if (error_type == blink::WebPushError::kErrorTypeNone) {
callbacks->OnSuccess(did_unsubscribe);
} else {
callbacks->OnError(blink::WebPushError(
error_type, blink::WebString::FromUTF8(error_message->c_str())));
}
}
void PushProvider::GetSubscription(
blink::WebServiceWorkerRegistration* service_worker_registration,
std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks) {
DCHECK(service_worker_registration);
DCHECK(callbacks);
int64_t service_worker_registration_id =
GetServiceWorkerRegistrationId(service_worker_registration);
push_messaging_manager_->GetSubscription(
service_worker_registration_id,
// Safe to use base::Unretained because |push_messaging_manager_ |is owned
// by |this|.
base::Bind(&PushProvider::DidGetSubscription, base::Unretained(this),
base::Passed(&callbacks)));
}
void PushProvider::DidGetSubscription(
std::unique_ptr<blink::WebPushSubscriptionCallbacks> callbacks,
content::PushGetRegistrationStatus status,
const base::Optional<GURL>& endpoint,
const base::Optional<content::PushSubscriptionOptions>& options,
const base::Optional<std::vector<uint8_t>>& p256dh,
const base::Optional<std::vector<uint8_t>>& auth) {
DCHECK(callbacks);
if (status == PUSH_GETREGISTRATION_STATUS_SUCCESS) {
DCHECK(endpoint);
DCHECK(options);
DCHECK(p256dh);
DCHECK(auth);
callbacks->OnSuccess(base::MakeUnique<blink::WebPushSubscription>(
endpoint.value(), options.value().user_visible_only,
blink::WebString::FromLatin1(options.value().sender_info),
p256dh.value(), auth.value()));
} else {
// We are only expecting an error if we can't find a registration.
callbacks->OnSuccess(nullptr);
}
}
void PushProvider::GetPermissionStatus(
blink::WebServiceWorkerRegistration* service_worker_registration,
const blink::WebPushSubscriptionOptions& options,
std::unique_ptr<blink::WebPushPermissionStatusCallbacks> callbacks) {
DCHECK(service_worker_registration);
DCHECK(callbacks);
int64_t service_worker_registration_id =
GetServiceWorkerRegistrationId(service_worker_registration);
push_messaging_manager_->GetPermissionStatus(
service_worker_registration_id, options.user_visible_only,
// Safe to use base::Unretained because |push_messaging_manager_ |is owned
// by |this|.
base::Bind(&PushProvider::DidGetPermissionStatus, base::Unretained(this),
base::Passed(&callbacks)));
}
void PushProvider::DidGetPermissionStatus(
std::unique_ptr<blink::WebPushPermissionStatusCallbacks> callbacks,
blink::WebPushError::ErrorType error_type,
blink::WebPushPermissionStatus status) {
DCHECK(callbacks);
// ErrorTypeNone indicates success.
if (error_type == blink::WebPushError::kErrorTypeNone) {
callbacks->OnSuccess(status);
} else {
std::string error_message;
if (error_type == blink::WebPushError::kErrorTypeNotSupported) {
error_message =
"Push subscriptions that don't enable userVisibleOnly are not "
"supported.";
}
callbacks->OnError(blink::WebPushError(
error_type, blink::WebString::FromUTF8(error_message)));
}
}
} // namespace content