blob: 473228014f3e066b3e0a9802b03af297416e81dc [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/browser/push_messaging/push_messaging_message_filter.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/common/push_messaging_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/push_messaging_service.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/push_messaging_status.h"
#include "third_party/WebKit/public/platform/modules/push_messaging/WebPushPermissionStatus.h"
namespace content {
const char kPushSenderIdServiceWorkerKey[] = "push_sender_id";
const char kPushRegistrationIdServiceWorkerKey[] = "push_registration_id";
namespace {
// These UMA methods are only called from IO thread, but it would be acceptable
// (even though slightly racy) to call them from UI thread as well, see
// https://groups.google.com/a/chromium.org/d/msg/chromium-dev/FNzZRJtN2aw/Aw0CWAXJJ1kJ
void RecordRegistrationStatus(PushRegistrationStatus status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UMA_HISTOGRAM_ENUMERATION("PushMessaging.RegistrationStatus",
status,
PUSH_REGISTRATION_STATUS_LAST + 1);
}
void RecordUnregistrationStatus(PushUnregistrationStatus status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UMA_HISTOGRAM_ENUMERATION("PushMessaging.UnregistrationStatus",
status,
PUSH_UNREGISTRATION_STATUS_LAST + 1);
}
void RecordGetRegistrationStatus(PushGetRegistrationStatus status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UMA_HISTOGRAM_ENUMERATION("PushMessaging.GetRegistrationStatus",
status,
PUSH_GETREGISTRATION_STATUS_LAST + 1);
}
// Concatenates the subscription id with the endpoint base to create a new
// GURL object containing the endpoint unique to the subscription.
GURL CreatePushEndpoint(const GURL& push_endpoint_base,
const std::string& push_subscription_id) {
return GURL(push_endpoint_base.spec() + "/" + push_subscription_id);
}
} // namespace
struct PushMessagingMessageFilter::RegisterData {
RegisterData();
RegisterData(const RegisterData& other) = default;
bool FromDocument() const;
int request_id;
GURL requesting_origin;
int64_t service_worker_registration_id;
bool user_visible;
// The following member should only be read if FromDocument() is true.
int render_frame_id;
};
// Inner core of this message filter which lives on the UI thread.
class PushMessagingMessageFilter::Core {
public:
Core(const base::WeakPtr<PushMessagingMessageFilter>& io_parent,
int render_process_id);
// Public Register methods on UI thread --------------------------------------
// Called via PostTask from IO thread.
void RegisterOnUI(const RegisterData& data, const std::string& sender_id);
// Public Unregister methods on UI thread ------------------------------------
// Called via PostTask from IO thread.
void UnregisterFromService(int request_id,
int64_t service_worker_registration_id,
const GURL& requesting_origin,
const std::string& sender_id);
// Public GetPermission methods on UI thread ---------------------------------
// Called via PostTask from IO thread.
void GetPermissionStatusOnUI(const GURL& requesting_origin,
bool user_visible,
int request_id);
// Public helper methods on UI thread ----------------------------------------
// Called (directly) from both the UI and IO threads.
bool is_incognito() const { return is_incognito_; }
// Returns a push messaging service. May return null.
PushMessagingService* service();
private:
friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
friend class base::DeleteHelper<Core>;
~Core();
// Private Register methods on UI thread -------------------------------------
void DidRegister(const RegisterData& data,
const std::string& push_registration_id,
PushRegistrationStatus status);
// Private Unregister methods on UI thread -----------------------------------
void DidUnregisterFromService(int request_id,
int64_t service_worker_registration_id,
PushUnregistrationStatus unregistration_status);
// Private helper methods on UI thread ---------------------------------------
void Send(IPC::Message* message);
// Outer part of this message filter which lives on the IO thread.
base::WeakPtr<PushMessagingMessageFilter> io_parent_;
int render_process_id_;
bool is_incognito_;
base::WeakPtrFactory<Core> weak_factory_ui_to_ui_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
PushMessagingMessageFilter::RegisterData::RegisterData()
: request_id(0),
service_worker_registration_id(0),
user_visible(false),
render_frame_id(ChildProcessHost::kInvalidUniqueID) {
}
bool PushMessagingMessageFilter::RegisterData::FromDocument() const {
return render_frame_id != ChildProcessHost::kInvalidUniqueID;
}
PushMessagingMessageFilter::Core::Core(
const base::WeakPtr<PushMessagingMessageFilter>& io_parent,
int render_process_id)
: io_parent_(io_parent),
render_process_id_(render_process_id),
weak_factory_ui_to_ui_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* process_host =
RenderProcessHost::FromID(render_process_id_); // Can't be null yet.
is_incognito_ = process_host->GetBrowserContext()->IsOffTheRecord();
}
PushMessagingMessageFilter::Core::~Core() {}
PushMessagingMessageFilter::PushMessagingMessageFilter(
int render_process_id,
ServiceWorkerContextWrapper* service_worker_context)
: BrowserMessageFilter(PushMessagingMsgStart),
service_worker_context_(service_worker_context),
weak_factory_io_to_io_(this) {
// Although this class is used only on the IO thread, it is constructed on UI.
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Normally, it would be unsafe to obtain a weak pointer from the UI thread,
// but it's ok in the constructor since we can't be destroyed before our
// constructor finishes.
ui_core_.reset(new Core(weak_factory_io_to_io_.GetWeakPtr(),
render_process_id));
PushMessagingService* push_service = ui_core_->service();
if (push_service)
push_endpoint_base_ = push_service->GetPushEndpoint();
}
PushMessagingMessageFilter::~PushMessagingMessageFilter() {}
void PushMessagingMessageFilter::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
bool PushMessagingMessageFilter::OnMessageReceived(
const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PushMessagingMessageFilter, message)
IPC_MESSAGE_HANDLER(PushMessagingHostMsg_SubscribeFromDocument,
OnSubscribeFromDocument)
IPC_MESSAGE_HANDLER(PushMessagingHostMsg_SubscribeFromWorker,
OnSubscribeFromWorker)
IPC_MESSAGE_HANDLER(PushMessagingHostMsg_Unsubscribe,
OnUnsubscribe)
IPC_MESSAGE_HANDLER(PushMessagingHostMsg_GetRegistration, OnGetRegistration)
IPC_MESSAGE_HANDLER(PushMessagingHostMsg_GetPermissionStatus,
OnGetPermissionStatus)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
// Subscribe methods on both IO and UI threads, merged in order of use from
// PushMessagingMessageFilter and Core.
// -----------------------------------------------------------------------------
void PushMessagingMessageFilter::OnSubscribeFromDocument(
int render_frame_id,
int request_id,
const std::string& sender_id,
bool user_visible,
int64_t service_worker_registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(mvanouwerkerk): Validate arguments?
RegisterData data;
data.request_id = request_id;
data.service_worker_registration_id = service_worker_registration_id;
data.render_frame_id = render_frame_id;
data.user_visible = user_visible;
ServiceWorkerRegistration* service_worker_registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
if (!service_worker_registration ||
!service_worker_registration->active_version()) {
SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER);
return;
}
data.requesting_origin = service_worker_registration->pattern().GetOrigin();
service_worker_context_->StoreRegistrationUserData(
service_worker_registration_id,
data.requesting_origin,
kPushSenderIdServiceWorkerKey,
sender_id,
base::Bind(&PushMessagingMessageFilter::DidPersistSenderId,
weak_factory_io_to_io_.GetWeakPtr(),
data, sender_id));
}
void PushMessagingMessageFilter::OnSubscribeFromWorker(
int request_id,
int64_t service_worker_registration_id,
bool user_visible) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
RegisterData data;
data.request_id = request_id;
data.service_worker_registration_id = service_worker_registration_id;
data.user_visible = user_visible;
ServiceWorkerRegistration* service_worker_registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
if (!service_worker_registration) {
SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SERVICE_WORKER);
return;
}
data.requesting_origin = service_worker_registration->pattern().GetOrigin();
// This sender_id will be ignored; instead it will be fetched from storage.
CheckForExistingRegistration(data, std::string() /* sender_id */);
}
void PushMessagingMessageFilter::DidPersistSenderId(
const RegisterData& data,
const std::string& sender_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status != SERVICE_WORKER_OK)
SendRegisterError(data, PUSH_REGISTRATION_STATUS_STORAGE_ERROR);
else
CheckForExistingRegistration(data, sender_id);
}
void PushMessagingMessageFilter::CheckForExistingRegistration(
const RegisterData& data,
const std::string& sender_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->GetRegistrationUserData(
data.service_worker_registration_id,
kPushRegistrationIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DidCheckForExistingRegistration,
weak_factory_io_to_io_.GetWeakPtr(), data, sender_id));
}
void PushMessagingMessageFilter::DidCheckForExistingRegistration(
const RegisterData& data,
const std::string& sender_id,
const std::string& push_registration_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status == SERVICE_WORKER_OK) {
SendRegisterSuccess(data, PUSH_REGISTRATION_STATUS_SUCCESS_FROM_CACHE,
push_registration_id);
return;
}
// TODO(johnme): The spec allows the register algorithm to reject with an
// AbortError when accessing storage fails. Perhaps we should do that if
// service_worker_status != SERVICE_WORKER_ERROR_NOT_FOUND instead of
// attempting to do a fresh registration?
// https://w3c.github.io/push-api/#widl-PushRegistrationManager-register-Promise-PushRegistration
if (data.FromDocument()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
data, sender_id));
} else {
service_worker_context_->GetRegistrationUserData(
data.service_worker_registration_id,
kPushSenderIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DidGetSenderIdFromStorage,
weak_factory_io_to_io_.GetWeakPtr(), data));
}
}
void PushMessagingMessageFilter::DidGetSenderIdFromStorage(
const RegisterData& data,
const std::string& sender_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status != SERVICE_WORKER_OK) {
SendRegisterError(data, PUSH_REGISTRATION_STATUS_NO_SENDER_ID);
return;
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&Core::RegisterOnUI, base::Unretained(ui_core_.get()),
data, sender_id));
}
void PushMessagingMessageFilter::Core::RegisterOnUI(
const PushMessagingMessageFilter::RegisterData& data,
const std::string& sender_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PushMessagingService* push_service = service();
if (!push_service) {
if (!is_incognito()) {
// TODO(johnme): Might be better not to expose the API in this case.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::SendRegisterError,
io_parent_,
data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE));
} else {
// Prevent websites from detecting incognito mode, by emulating what would
// have happened if we had a PushMessagingService available.
if (!data.FromDocument() || !data.user_visible) {
// Throw a permission denied error under the same circumstances.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::SendRegisterError,
io_parent_,
data,
PUSH_REGISTRATION_STATUS_INCOGNITO_PERMISSION_DENIED));
}
// Else leave the promise hanging forever, to simulate a user ignoring the
// infobar. TODO(johnme): Simulate the user dismissing the infobar after a
// random time period.
}
return;
}
if (data.FromDocument()) {
push_service->SubscribeFromDocument(
data.requesting_origin, data.service_worker_registration_id, sender_id,
render_process_id_, data.render_frame_id, data.user_visible,
base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(),
data));
} else {
push_service->SubscribeFromWorker(
data.requesting_origin, data.service_worker_registration_id, sender_id,
data.user_visible,
base::Bind(&Core::DidRegister, weak_factory_ui_to_ui_.GetWeakPtr(),
data));
}
}
void PushMessagingMessageFilter::Core::DidRegister(
const RegisterData& data,
const std::string& push_registration_id,
PushRegistrationStatus status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status == PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::PersistRegistrationOnIO,
io_parent_, data, push_registration_id));
} else {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::SendRegisterError, io_parent_,
data, status));
}
}
void PushMessagingMessageFilter::PersistRegistrationOnIO(
const RegisterData& data,
const std::string& push_registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->StoreRegistrationUserData(
data.service_worker_registration_id,
data.requesting_origin,
kPushRegistrationIdServiceWorkerKey,
push_registration_id,
base::Bind(&PushMessagingMessageFilter::DidPersistRegistrationOnIO,
weak_factory_io_to_io_.GetWeakPtr(),
data, push_registration_id));
}
void PushMessagingMessageFilter::DidPersistRegistrationOnIO(
const RegisterData& data,
const std::string& push_registration_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status == SERVICE_WORKER_OK) {
SendRegisterSuccess(data,
PUSH_REGISTRATION_STATUS_SUCCESS_FROM_PUSH_SERVICE,
push_registration_id);
} else {
// TODO(johnme): Unregister, so PushMessagingServiceImpl can decrease count.
SendRegisterError(data, PUSH_REGISTRATION_STATUS_STORAGE_ERROR);
}
}
void PushMessagingMessageFilter::SendRegisterError(
const RegisterData& data, PushRegistrationStatus status) {
// Only called from IO thread, but would be safe to call from UI thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (data.FromDocument()) {
Send(new PushMessagingMsg_SubscribeFromDocumentError(
data.render_frame_id, data.request_id, status));
} else {
Send(new PushMessagingMsg_SubscribeFromWorkerError(
data.request_id, status));
}
RecordRegistrationStatus(status);
}
void PushMessagingMessageFilter::SendRegisterSuccess(
const RegisterData& data,
PushRegistrationStatus status,
const std::string& push_registration_id) {
// Only called from IO thread, but would be safe to call from UI thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (push_endpoint_base_.is_empty()) {
// This shouldn't be possible in incognito mode, since we've already checked
// that we have an existing registration. Hence it's ok to throw an error.
DCHECK(!ui_core_->is_incognito());
SendRegisterError(data, PUSH_REGISTRATION_STATUS_SERVICE_NOT_AVAILABLE);
return;
}
if (data.FromDocument()) {
Send(new PushMessagingMsg_SubscribeFromDocumentSuccess(
data.render_frame_id,
data.request_id,
CreatePushEndpoint(push_endpoint_base_, push_registration_id)));
} else {
Send(new PushMessagingMsg_SubscribeFromWorkerSuccess(
data.request_id,
CreatePushEndpoint(push_endpoint_base_, push_registration_id)));
}
RecordRegistrationStatus(status);
}
// Unsubscribe methods on both IO and UI threads, merged in order of use from
// PushMessagingMessageFilter and Core.
// -----------------------------------------------------------------------------
void PushMessagingMessageFilter::OnUnsubscribe(
int request_id, int64_t service_worker_registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerRegistration* service_worker_registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
if (!service_worker_registration) {
DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER);
return;
}
service_worker_context_->GetRegistrationUserData(
service_worker_registration_id,
kPushRegistrationIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::
UnsubscribeHavingGottenPushSubscriptionId,
weak_factory_io_to_io_.GetWeakPtr(), request_id,
service_worker_registration_id,
service_worker_registration->pattern().GetOrigin()));
}
void PushMessagingMessageFilter::UnsubscribeHavingGottenPushSubscriptionId(
int request_id,
int64_t service_worker_registration_id,
const GURL& requesting_origin,
const std::string& push_subscription_id, // Unused, we just want the status
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status == SERVICE_WORKER_OK) {
service_worker_context_->GetRegistrationUserData(
service_worker_registration_id,
kPushSenderIdServiceWorkerKey,
base::Bind(
&PushMessagingMessageFilter::UnsubscribeHavingGottenSenderId,
weak_factory_io_to_io_.GetWeakPtr(),
request_id,
service_worker_registration_id,
requesting_origin));
} else {
// Errors are handled the same, whether we were trying to get the
// push_subscription_id or the sender_id.
UnsubscribeHavingGottenSenderId(request_id, service_worker_registration_id,
requesting_origin,
std::string() /* sender_id */,
service_worker_status);
}
}
void PushMessagingMessageFilter::UnsubscribeHavingGottenSenderId(
int request_id,
int64_t service_worker_registration_id,
const GURL& requesting_origin,
const std::string& sender_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
switch (service_worker_status) {
case SERVICE_WORKER_OK:
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&Core::UnregisterFromService,
base::Unretained(ui_core_.get()), request_id,
service_worker_registration_id, requesting_origin,
sender_id));
break;
case SERVICE_WORKER_ERROR_NOT_FOUND:
// We did not find a registration, stop here and notify the renderer that
// it was a success even though we did not unregister.
DidUnregister(request_id,
PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED);
break;
case SERVICE_WORKER_ERROR_FAILED:
DidUnregister(request_id,
PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR);
break;
case SERVICE_WORKER_ERROR_ABORT:
case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
case SERVICE_WORKER_ERROR_EXISTS:
case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
case SERVICE_WORKER_ERROR_IPC_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
case SERVICE_WORKER_ERROR_TIMEOUT:
case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
case SERVICE_WORKER_ERROR_DISK_CACHE:
case SERVICE_WORKER_ERROR_MAX_VALUE:
NOTREACHED() << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
DidUnregister(request_id, PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR);
break;
}
}
void PushMessagingMessageFilter::Core::UnregisterFromService(
int request_id,
int64_t service_worker_registration_id,
const GURL& requesting_origin,
const std::string& sender_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
PushMessagingService* push_service = service();
if (!push_service) {
// This shouldn't be possible in incognito mode, since we've already checked
// that we have an existing registration. Hence it's ok to throw an error.
DCHECK(!is_incognito());
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::DidUnregister, io_parent_,
request_id,
PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE));
return;
}
push_service->Unsubscribe(
requesting_origin, service_worker_registration_id, sender_id,
base::Bind(&Core::DidUnregisterFromService,
weak_factory_ui_to_ui_.GetWeakPtr(),
request_id, service_worker_registration_id));
}
void PushMessagingMessageFilter::Core::DidUnregisterFromService(
int request_id,
int64_t service_worker_registration_id,
PushUnregistrationStatus unregistration_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
switch (unregistration_status) {
case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED:
case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED:
case PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR:
case PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR:
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::ClearRegistrationData,
io_parent_, request_id, service_worker_registration_id,
unregistration_status));
break;
case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER:
case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR:
case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR:
NOTREACHED();
break;
}
}
void PushMessagingMessageFilter::ClearRegistrationData(
int request_id,
int64_t service_worker_registration_id,
PushUnregistrationStatus unregistration_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->ClearRegistrationUserData(
service_worker_registration_id,
kPushRegistrationIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DidClearRegistrationData,
weak_factory_io_to_io_.GetWeakPtr(),
request_id, unregistration_status));
}
void PushMessagingMessageFilter::DidClearRegistrationData(
int request_id,
PushUnregistrationStatus unregistration_status,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (service_worker_status != SERVICE_WORKER_OK &&
service_worker_status != SERVICE_WORKER_ERROR_NOT_FOUND) {
unregistration_status = PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR;
DLOG(WARNING) << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
}
DidUnregister(request_id, unregistration_status);
}
void PushMessagingMessageFilter::DidUnregister(
int request_id,
PushUnregistrationStatus unregistration_status) {
// Only called from IO thread, but would be safe to call from UI thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
switch (unregistration_status) {
case PUSH_UNREGISTRATION_STATUS_SUCCESS_UNREGISTERED:
case PUSH_UNREGISTRATION_STATUS_PENDING_NETWORK_ERROR:
case PUSH_UNREGISTRATION_STATUS_PENDING_SERVICE_ERROR:
Send(new PushMessagingMsg_UnsubscribeSuccess(request_id, true));
break;
case PUSH_UNREGISTRATION_STATUS_SUCCESS_WAS_NOT_REGISTERED:
Send(new PushMessagingMsg_UnsubscribeSuccess(request_id, false));
break;
case PUSH_UNREGISTRATION_STATUS_NO_SERVICE_WORKER:
case PUSH_UNREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE:
case PUSH_UNREGISTRATION_STATUS_STORAGE_ERROR:
Send(new PushMessagingMsg_UnsubscribeError(
request_id, blink::WebPushError::ErrorTypeAbort,
PushUnregistrationStatusToString(unregistration_status)));
break;
case PUSH_UNREGISTRATION_STATUS_NETWORK_ERROR:
NOTREACHED();
break;
}
RecordUnregistrationStatus(unregistration_status);
}
// GetRegistration methods on both IO and UI threads, merged in order of use
// from PushMessagingMessageFilter and Core.
// -----------------------------------------------------------------------------
void PushMessagingMessageFilter::OnGetRegistration(
int request_id,
int64_t service_worker_registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// TODO(johnme): Validate arguments?
service_worker_context_->GetRegistrationUserData(
service_worker_registration_id,
kPushRegistrationIdServiceWorkerKey,
base::Bind(&PushMessagingMessageFilter::DidGetRegistration,
weak_factory_io_to_io_.GetWeakPtr(), request_id));
}
void PushMessagingMessageFilter::DidGetRegistration(
int request_id,
const std::string& push_registration_id,
ServiceWorkerStatusCode service_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
PushGetRegistrationStatus get_status =
PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
switch (service_worker_status) {
case SERVICE_WORKER_OK:
if (push_endpoint_base_.is_empty()) {
// Return not found in incognito mode, so websites can't detect it.
get_status =
ui_core_->is_incognito()
? PUSH_GETREGISTRATION_STATUS_INCOGNITO_REGISTRATION_NOT_FOUND
: PUSH_GETREGISTRATION_STATUS_SERVICE_NOT_AVAILABLE;
break;
}
Send(new PushMessagingMsg_GetRegistrationSuccess(
request_id,
CreatePushEndpoint(push_endpoint_base_, push_registration_id)));
RecordGetRegistrationStatus(PUSH_GETREGISTRATION_STATUS_SUCCESS);
return;
case SERVICE_WORKER_ERROR_NOT_FOUND:
get_status = PUSH_GETREGISTRATION_STATUS_REGISTRATION_NOT_FOUND;
break;
case SERVICE_WORKER_ERROR_FAILED:
get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
break;
case SERVICE_WORKER_ERROR_ABORT:
case SERVICE_WORKER_ERROR_START_WORKER_FAILED:
case SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND:
case SERVICE_WORKER_ERROR_EXISTS:
case SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED:
case SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED:
case SERVICE_WORKER_ERROR_IPC_FAILED:
case SERVICE_WORKER_ERROR_NETWORK:
case SERVICE_WORKER_ERROR_SECURITY:
case SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED:
case SERVICE_WORKER_ERROR_STATE:
case SERVICE_WORKER_ERROR_TIMEOUT:
case SERVICE_WORKER_ERROR_SCRIPT_EVALUATE_FAILED:
case SERVICE_WORKER_ERROR_DISK_CACHE:
case SERVICE_WORKER_ERROR_MAX_VALUE:
NOTREACHED() << "Got unexpected error code: " << service_worker_status
<< " " << ServiceWorkerStatusToString(service_worker_status);
get_status = PUSH_GETREGISTRATION_STATUS_STORAGE_ERROR;
break;
}
Send(new PushMessagingMsg_GetRegistrationError(request_id, get_status));
RecordGetRegistrationStatus(get_status);
}
// GetPermission methods on both IO and UI threads, merged in order of use from
// PushMessagingMessageFilter and Core.
// -----------------------------------------------------------------------------
void PushMessagingMessageFilter::OnGetPermissionStatus(
int request_id,
int64_t service_worker_registration_id,
bool user_visible) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
ServiceWorkerRegistration* service_worker_registration =
service_worker_context_->GetLiveRegistration(
service_worker_registration_id);
if (!service_worker_registration) {
Send(new PushMessagingMsg_GetPermissionStatusError(
request_id, blink::WebPushError::ErrorTypeAbort));
return;
}
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&Core::GetPermissionStatusOnUI,
base::Unretained(ui_core_.get()),
service_worker_registration->pattern().GetOrigin(),
user_visible, request_id));
}
void PushMessagingMessageFilter::Core::GetPermissionStatusOnUI(
const GURL& requesting_origin, bool user_visible, int request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
blink::WebPushPermissionStatus permission_status;
PushMessagingService* push_service = service();
if (push_service) {
if (!user_visible && !push_service->SupportNonVisibleMessages()) {
Send(new PushMessagingMsg_GetPermissionStatusError(
request_id, blink::WebPushError::ErrorTypeNotSupported));
return;
}
GURL embedding_origin = requesting_origin;
permission_status = push_service->GetPermissionStatus(requesting_origin,
embedding_origin,
user_visible);
} else if (is_incognito()) {
// Return prompt, so the website can't detect incognito mode.
permission_status = blink::WebPushPermissionStatusPrompt;
} else {
Send(new PushMessagingMsg_GetPermissionStatusError(
request_id, blink::WebPushError::ErrorTypeAbort));
return;
}
Send(new PushMessagingMsg_GetPermissionStatusSuccess(request_id,
permission_status));
}
// Helper methods on both IO and UI threads, merged from
// PushMessagingMessageFilter and Core.
// -----------------------------------------------------------------------------
void PushMessagingMessageFilter::Core::Send(IPC::Message* message) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PushMessagingMessageFilter::SendIPC, io_parent_,
base::Passed(make_scoped_ptr(message))));
}
void PushMessagingMessageFilter::SendIPC(scoped_ptr<IPC::Message> message) {
Send(message.release());
}
PushMessagingService* PushMessagingMessageFilter::Core::service() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RenderProcessHost* process_host =
RenderProcessHost::FromID(render_process_id_);
return process_host
? process_host->GetBrowserContext()->GetPushMessagingService()
: nullptr;
}
} // namespace content