Bug 1275434 - Refactor PushNotifier to clarify remoting logic. r=dragana

Previously, all `PushNotifier` methods checked the process type, and
either called `Notify*{Workers, Observers}` or sent an IPDL message.
The message handlers then called back in to `PushNotifier` from the
remote process.

This was clearer when we only sent worker event notifications to the
content process, and fired all observer notifications in the parent.
It became more complicated once we started notifying observers for all
subscriptions in both processes (bug 1266433). This makes it harder to
see omissions; for example, "push-subscription-modified" isn't
currently forwarded to the child, but "push-subscription-change" and
"push-message" are.

This patch moves the remoting code into `PushNotifier::Dispatch`, and
adds a base `PushDispatcher` class. Each notification type subclasses
this class and implements logic for sending messages and firing
observers and worker events. It's more code, but a bit easier to see
which methods are called where.

MozReview-Commit-ID: 7Q0eD7qXOrW

--HG--
extra : rebase_source : c69acb95a0cb5470cf1c804639971be41b976cc2
This commit is contained in:
Kit Cambridge 2016-05-19 19:01:34 -07:00
parent 858cf1d807
commit 21fdc3d97d
7 changed files with 468 additions and 399 deletions

View File

@ -6,9 +6,6 @@
#include "nsISupports.idl"
%{C++
#include "nsTArray.h"
#include "mozilla/Maybe.h"
#define PUSHNOTIFIER_CONTRACTID \
"@mozilla.org/push/Notifier;1"
@ -20,8 +17,6 @@
interface nsIPrincipal;
[ref] native MaybeData(const mozilla::Maybe<nsTArray<uint8_t>>);
/**
* Fires XPCOM observer notifications and service worker events for
* messages sent to push subscriptions.
@ -67,37 +62,6 @@ interface nsIPushNotifier : nsISupports
void notifyError(in ACString scope, in nsIPrincipal principal,
in DOMString message, in uint32_t flags);
/**
* Internal methods used to fire service worker events and observer
* notifications. These are not exposed to JavaScript.
*/
[noscript, nostdcall]
void notifyPushWorkers(in ACString scope,
in nsIPrincipal principal,
in DOMString messageId,
in MaybeData data);
[noscript, nostdcall]
void notifyPushObservers(in ACString scope, in nsIPrincipal principal,
in MaybeData data);
[noscript, nostdcall]
void notifySubscriptionChangeWorkers(in ACString scope,
in nsIPrincipal principal);
[noscript, nostdcall]
void notifySubscriptionChangeObservers(in ACString scope,
in nsIPrincipal principal);
[noscript, nostdcall]
void notifySubscriptionModifiedObservers(in ACString scope,
in nsIPrincipal principal);
[noscript, nostdcall, notxpcom]
void notifyErrorWorkers(in ACString scope, in DOMString message,
in uint32_t flags);
};
/**

View File

@ -176,7 +176,7 @@
#endif
#ifndef MOZ_SIMPLEPUSH
#include "nsIPushNotifier.h"
#include "mozilla/dom/PushNotifier.h"
#endif
#include "mozilla/dom/File.h"
@ -3285,19 +3285,8 @@ ContentChild::RecvPush(const nsCString& aScope,
const nsString& aMessageId)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
Nothing());
Unused << NS_WARN_IF(NS_FAILED(rv));
rv = pushNotifier->NotifyPushWorkers(aScope, aPrincipal,
aMessageId, Nothing());
Unused << NS_WARN_IF(NS_FAILED(rv));
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
#endif
return true;
}
@ -3309,19 +3298,8 @@ ContentChild::RecvPushWithData(const nsCString& aScope,
InfallibleTArray<uint8_t>&& aData)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
Some(aData));
Unused << NS_WARN_IF(NS_FAILED(rv));
rv = pushNotifier->NotifyPushWorkers(aScope, aPrincipal,
aMessageId, Some(aData));
Unused << NS_WARN_IF(NS_FAILED(rv));
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
#endif
return true;
}
@ -3331,33 +3309,19 @@ ContentChild::RecvPushSubscriptionChange(const nsCString& aScope,
const IPC::Principal& aPrincipal)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope,
aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
rv = pushNotifier->NotifySubscriptionChangeWorkers(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
#endif
return true;
}
bool
ContentChild::RecvPushError(const nsCString& aScope, const nsString& aMessage,
const uint32_t& aFlags)
ContentChild::RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
const nsString& aMessage, const uint32_t& aFlags)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
pushNotifier->NotifyErrorWorkers(aScope, aMessage, aFlags);
PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
#endif
return true;
}

View File

@ -539,8 +539,8 @@ public:
const IPC::Principal& aPrincipal) override;
virtual bool
RecvPushError(const nsCString& aScope, const nsString& aMessage,
const uint32_t& aFlags) override;
RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
const nsString& aMessage, const uint32_t& aFlags) override;
// Get the directory for IndexedDB files. We query the parent for this and
// cache the value

View File

@ -267,7 +267,7 @@ using namespace mozilla::system;
#endif
#ifndef MOZ_SIMPLEPUSH
#include "nsIPushNotifier.h"
#include "mozilla/dom/PushNotifier.h"
#endif
#ifdef XP_WIN
@ -5828,15 +5828,8 @@ ContentParent::RecvNotifyPushObservers(const nsCString& aScope,
const nsString& aMessageId)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
Nothing());
Unused << NS_WARN_IF(NS_FAILED(rv));
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
#endif
return true;
}
@ -5848,15 +5841,8 @@ ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope,
InfallibleTArray<uint8_t>&& aData)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
Some(aData));
Unused << NS_WARN_IF(NS_FAILED(rv));
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
#endif
return true;
}
@ -5866,15 +5852,8 @@ ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope
const IPC::Principal& aPrincipal)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope,
aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
#endif
return true;
}
@ -5884,15 +5863,8 @@ ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aSco
const IPC::Principal& aPrincipal)
{
#ifndef MOZ_SIMPLEPUSH
nsCOMPtr<nsIPushNotifier> pushNotifier =
do_GetService("@mozilla.org/push/Notifier;1");
if (NS_WARN_IF(!pushNotifier)) {
return true;
}
nsresult rv = pushNotifier->NotifySubscriptionModifiedObservers(aScope,
aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
#endif
return true;
}

View File

@ -672,7 +672,8 @@ child:
/**
* Send a Push error message to all service worker clients in the child.
*/
async PushError(nsCString scope, nsString message, uint32_t flags);
async PushError(nsCString scope, Principal principal, nsString message,
uint32_t flags);
/**
* Windows specific: associate this content process with the browsers

View File

@ -54,312 +54,82 @@ PushNotifier::NotifyPushWithData(const nsACString& aScope,
if (!data.InsertElementsAt(0, aData, aDataLen, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NotifyPush(aScope, aPrincipal, aMessageId, Some(data));
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(data));
return Dispatch(dispatcher);
}
NS_IMETHODIMP
PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
const nsAString& aMessageId)
{
return NotifyPush(aScope, aPrincipal, aMessageId, Nothing());
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
return Dispatch(dispatcher);
}
NS_IMETHODIMP
PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
nsresult rv = NotifySubscriptionChangeObservers(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
if (XRE_IsContentProcess()) {
// Forward XPCOM observer notifications to the parent.
ContentChild* parentActor = ContentChild::GetSingleton();
if (!NS_WARN_IF(!parentActor)) {
Unused << NS_WARN_IF(
!parentActor->SendNotifyPushSubscriptionChangeObservers(
PromiseFlatCString(aScope), IPC::Principal(aPrincipal)));
}
}
rv = NotifySubscriptionChangeWorkers(aScope, aPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
return Dispatch(dispatcher);
}
NS_IMETHODIMP
PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
nsresult rv = NotifySubscriptionModifiedObservers(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(rv));
if (XRE_IsContentProcess()) {
ContentChild* parentActor = ContentChild::GetSingleton();
if (!NS_WARN_IF(!parentActor)) {
Unused << NS_WARN_IF(
!parentActor->SendNotifyPushSubscriptionModifiedObservers(
PromiseFlatCString(aScope), IPC::Principal(aPrincipal)));
}
}
return NS_OK;
PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
return Dispatch(dispatcher);
}
NS_IMETHODIMP
PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
const nsAString& aMessage, uint32_t aFlags)
{
if (ShouldNotifyWorkers(aPrincipal)) {
// For service worker subscriptions, report the error to all clients.
NotifyErrorWorkers(aScope, aMessage, aFlags);
return NS_OK;
}
// For system subscriptions, log the error directly to the browser console.
return nsContentUtils::ReportToConsoleNonLocalized(aMessage,
aFlags,
NS_LITERAL_CSTRING("Push"),
nullptr, /* aDocument */
nullptr, /* aURI */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
nsContentUtils::eOMIT_LOCATION);
PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
return Dispatch(dispatcher);
}
nsresult
PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData)
PushNotifier::Dispatch(PushDispatcher& aDispatcher)
{
// Notify XPCOM observers in the current process.
nsresult rv = NotifyPushObservers(aScope, aPrincipal, aData);
Unused << NS_WARN_IF(NS_FAILED(rv));
if (XRE_IsParentProcess()) {
// Always notify XPCOM observers in the parent process.
Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
if (XRE_IsContentProcess()) {
// If we're in the content process, forward the notification to the parent.
// We don't need to do anything if we're already in the parent;
// `ContentChild::RecvPush` will notify content process observers.
ContentChild* parentActor = ContentChild::GetSingleton();
if (!NS_WARN_IF(!parentActor)) {
if (aData) {
Unused << NS_WARN_IF(
!parentActor->SendNotifyPushObserversWithData(
PromiseFlatCString(aScope), IPC::Principal(aPrincipal),
PromiseFlatString(aMessageId), aData.ref()));
} else {
Unused << NS_WARN_IF(
!parentActor->SendNotifyPushObservers(
PromiseFlatCString(aScope), IPC::Principal(aPrincipal),
PromiseFlatString(aMessageId)));
nsTArray<ContentParent*> contentActors;
ContentParent::GetAll(contentActors);
if (!contentActors.IsEmpty()) {
// At least one content process is active, so e10s must be enabled.
// Broadcast a message to notify observers and service workers.
for (uint32_t i = 0; i < contentActors.Length(); ++i) {
Unused << NS_WARN_IF(!aDispatcher.SendToChild(contentActors[i]));
}
}
}
rv = NotifyPushWorkers(aScope, aPrincipal, aMessageId, aData);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
PushNotifier::NotifyPushWorkers(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData)
{
AssertIsOnMainThread();
NS_ENSURE_ARG(aPrincipal);
if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
// Notify the worker from the current process. Either we're running in
// the content process and received a message from the parent, or e10s
// is disabled.
if (!ShouldNotifyWorkers(aPrincipal)) {
return NS_OK;
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
if (BrowserTabsRemoteAutostart()) {
// e10s is enabled, but no content processes are active.
return aDispatcher.HandleNoChildProcesses();
}
nsAutoCString originSuffix;
nsresult rv = aPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return swm->SendPushEvent(originSuffix, aScope, aMessageId, aData);
// e10s is disabled; notify workers in the parent.
return aDispatcher.NotifyWorkers();
}
// Otherwise, we're in the parent and e10s is enabled. Broadcast the event
// to all content processes.
bool ok = true;
nsTArray<ContentParent*> contentActors;
ContentParent::GetAll(contentActors);
for (uint32_t i = 0; i < contentActors.Length(); ++i) {
if (aData) {
ok &= contentActors[i]->SendPushWithData(PromiseFlatCString(aScope),
IPC::Principal(aPrincipal), PromiseFlatString(aMessageId), aData.ref());
} else {
ok &= contentActors[i]->SendPush(PromiseFlatCString(aScope),
IPC::Principal(aPrincipal), PromiseFlatString(aMessageId));
}
}
return ok ? NS_OK : NS_ERROR_FAILURE;
}
// Otherwise, we're in the content process, so e10s must be enabled. Notify
// observers and workers, then send a message to notify observers in the
// parent.
MOZ_ASSERT(XRE_IsContentProcess());
nsresult
PushNotifier::NotifySubscriptionChangeWorkers(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
AssertIsOnMainThread();
NS_ENSURE_ARG(aPrincipal);
nsresult rv = aDispatcher.NotifyObserversAndWorkers();
if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
// Content process or e10s disabled.
if (!ShouldNotifyWorkers(aPrincipal)) {
return NS_OK;
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
}
nsAutoCString originSuffix;
nsresult rv = aPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return swm->SendPushSubscriptionChangeEvent(originSuffix, aScope);
ContentChild* parentActor = ContentChild::GetSingleton();
if (!NS_WARN_IF(!parentActor)) {
Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
}
// Parent process, e10s enabled.
bool ok = true;
nsTArray<ContentParent*> contentActors;
ContentParent::GetAll(contentActors);
for (uint32_t i = 0; i < contentActors.Length(); ++i) {
ok &= contentActors[i]->SendPushSubscriptionChange(
PromiseFlatCString(aScope), IPC::Principal(aPrincipal));
}
return ok ? NS_OK : NS_ERROR_FAILURE;
}
void
PushNotifier::NotifyErrorWorkers(const nsACString& aScope,
const nsAString& aMessage,
uint32_t aFlags)
{
AssertIsOnMainThread();
if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
// Content process or e10s disabled.
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (swm) {
swm->ReportToAllClients(PromiseFlatCString(aScope),
PromiseFlatString(aMessage),
NS_ConvertUTF8toUTF16(aScope), /* aFilename */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
aFlags);
}
return;
}
// Parent process, e10s enabled.
nsTArray<ContentParent*> contentActors;
ContentParent::GetAll(contentActors);
if (!contentActors.IsEmpty()) {
// At least one content process active.
for (uint32_t i = 0; i < contentActors.Length(); ++i) {
Unused << NS_WARN_IF(
!contentActors[i]->SendPushError(PromiseFlatCString(aScope),
PromiseFlatString(aMessage), aFlags));
}
return;
}
// Report to the console if no content processes are active.
nsCOMPtr<nsIURI> scopeURI;
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
Unused << NS_WARN_IF(NS_FAILED(
nsContentUtils::ReportToConsoleNonLocalized(aMessage,
aFlags,
NS_LITERAL_CSTRING("Push"),
nullptr, /* aDocument */
scopeURI, /* aURI */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
nsContentUtils::eOMIT_LOCATION)));
}
nsresult
PushNotifier::NotifyPushObservers(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const Maybe<nsTArray<uint8_t>>& aData)
{
nsCOMPtr<nsIPushData> data;
if (aData) {
data = new PushData(aData.ref());
}
nsCOMPtr<nsIPushMessage> message = new PushMessage(aPrincipal, data);
return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, aScope);
}
nsresult
PushNotifier::NotifySubscriptionChangeObservers(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
return DoNotifyObservers(aPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
aScope);
}
nsresult
PushNotifier::NotifySubscriptionModifiedObservers(const nsACString& aScope,
nsIPrincipal* aPrincipal)
{
return DoNotifyObservers(aPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
aScope);
}
nsresult
PushNotifier::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope)
{
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (!obsService) {
return NS_ERROR_FAILURE;
}
// If there's a service for this push category, make sure it is alive.
nsCOMPtr<nsICategoryManager> catMan =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catMan) {
nsXPIDLCString contractId;
nsresult rv = catMan->GetCategoryEntry("push",
PromiseFlatCString(aScope).get(),
getter_Copies(contractId));
if (NS_SUCCEEDED(rv)) {
// Ensure the service is created - we don't need to do anything with
// it though - we assume the service constructor attaches a listener.
nsCOMPtr<nsISupports> service = do_GetService(contractId);
}
}
return obsService->NotifyObservers(aSubject, aTopic,
NS_ConvertUTF8toUTF16(aScope).get());
}
bool
PushNotifier::ShouldNotifyWorkers(nsIPrincipal* aPrincipal)
{
// System subscriptions use observer notifications instead of service worker
// events. The `testing.notifyWorkers` pref disables worker events for
// non-system subscriptions.
return !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
Preferences::GetBool("dom.push.testing.notifyWorkers", true);
return rv;
}
PushData::PushData(const nsTArray<uint8_t>& aData)
@ -481,5 +251,291 @@ PushMessage::GetData(nsIPushData** aData)
return NS_OK;
}
PushDispatcher::PushDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal)
: mScope(aScope)
, mPrincipal(aPrincipal)
{}
PushDispatcher::~PushDispatcher()
{}
nsresult
PushDispatcher::HandleNoChildProcesses()
{
return NS_OK;
}
nsresult
PushDispatcher::NotifyObserversAndWorkers()
{
Unused << NS_WARN_IF(NS_FAILED(NotifyObservers()));
return NotifyWorkers();
}
bool
PushDispatcher::ShouldNotifyWorkers()
{
// System subscriptions use observer notifications instead of service worker
// events. The `testing.notifyWorkers` pref disables worker events for
// non-system subscriptions.
return !nsContentUtils::IsSystemPrincipal(mPrincipal) &&
Preferences::GetBool("dom.push.testing.notifyWorkers", true);
}
nsresult
PushDispatcher::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope)
{
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (!obsService) {
return NS_ERROR_FAILURE;
}
// If there's a service for this push category, make sure it is alive.
nsCOMPtr<nsICategoryManager> catMan =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catMan) {
nsXPIDLCString contractId;
nsresult rv = catMan->GetCategoryEntry("push",
mScope.BeginReading(),
getter_Copies(contractId));
if (NS_SUCCEEDED(rv)) {
// Ensure the service is created - we don't need to do anything with
// it though - we assume the service constructor attaches a listener.
nsCOMPtr<nsISupports> service = do_GetService(contractId);
}
}
return obsService->NotifyObservers(aSubject, aTopic,
NS_ConvertUTF8toUTF16(mScope).get());
}
PushMessageDispatcher::PushMessageDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData)
: PushDispatcher(aScope, aPrincipal)
, mMessageId(aMessageId)
, mData(aData)
{}
PushMessageDispatcher::~PushMessageDispatcher()
{}
nsresult
PushMessageDispatcher::NotifyObservers()
{
nsCOMPtr<nsIPushData> data;
if (mData) {
data = new PushData(mData.ref());
}
nsCOMPtr<nsIPushMessage> message = new PushMessage(mPrincipal, data);
return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, mScope);
}
nsresult
PushMessageDispatcher::NotifyWorkers()
{
if (!ShouldNotifyWorkers()) {
return NS_OK;
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
}
nsAutoCString originSuffix;
nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return swm->SendPushEvent(originSuffix, mScope, mMessageId, mData);
}
bool
PushMessageDispatcher::SendToParent(ContentChild* aParentActor)
{
if (mData) {
return aParentActor->SendNotifyPushObserversWithData(mScope,
IPC::Principal(mPrincipal),
mMessageId,
mData.ref());
}
return aParentActor->SendNotifyPushObservers(mScope,
IPC::Principal(mPrincipal),
mMessageId);
}
bool
PushMessageDispatcher::SendToChild(ContentParent* aContentActor)
{
if (mData) {
return aContentActor->SendPushWithData(mScope, IPC::Principal(mPrincipal),
mMessageId, mData.ref());
}
return aContentActor->SendPush(mScope, IPC::Principal(mPrincipal),
mMessageId);
}
PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal)
: PushDispatcher(aScope, aPrincipal)
{}
PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher()
{}
nsresult
PushSubscriptionChangeDispatcher::NotifyObservers()
{
return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
mScope);
}
nsresult
PushSubscriptionChangeDispatcher::NotifyWorkers()
{
if (!ShouldNotifyWorkers()) {
return NS_OK;
}
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
return NS_ERROR_FAILURE;
}
nsAutoCString originSuffix;
nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
}
bool
PushSubscriptionChangeDispatcher::SendToParent(ContentChild* aParentActor)
{
return aParentActor->SendNotifyPushSubscriptionChangeObservers(mScope,
IPC::Principal(mPrincipal));
}
bool
PushSubscriptionChangeDispatcher::SendToChild(ContentParent* aContentActor)
{
return aContentActor->SendPushSubscriptionChange(mScope,
IPC::Principal(mPrincipal));
}
PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal)
: PushDispatcher(aScope, aPrincipal)
{}
PushSubscriptionModifiedDispatcher::~PushSubscriptionModifiedDispatcher()
{}
nsresult
PushSubscriptionModifiedDispatcher::NotifyObservers()
{
return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
mScope);
}
nsresult
PushSubscriptionModifiedDispatcher::NotifyWorkers()
{
return NS_OK;
}
bool
PushSubscriptionModifiedDispatcher::SendToParent(ContentChild* aParentActor)
{
return aParentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
IPC::Principal(mPrincipal));
}
bool
PushSubscriptionModifiedDispatcher::SendToChild(ContentParent*)
{
return true;
}
PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aMessage,
uint32_t aFlags)
: PushDispatcher(aScope, aPrincipal)
, mMessage(aMessage)
, mFlags(aFlags)
{}
PushErrorDispatcher::~PushErrorDispatcher()
{}
nsresult
PushErrorDispatcher::NotifyObservers()
{
return NS_OK;
}
nsresult
PushErrorDispatcher::NotifyWorkers()
{
if (!ShouldNotifyWorkers()) {
// For system subscriptions, log the error directly to the browser console.
return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
mFlags,
NS_LITERAL_CSTRING("Push"),
nullptr, /* aDocument */
nullptr, /* aURI */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
nsContentUtils::eOMIT_LOCATION);
}
// For service worker subscriptions, report the error to all clients.
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (swm) {
swm->ReportToAllClients(mScope,
mMessage,
NS_ConvertUTF8toUTF16(mScope), /* aFilename */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
mFlags);
}
return NS_OK;
}
bool
PushErrorDispatcher::SendToParent(ContentChild*)
{
return true;
}
bool
PushErrorDispatcher::SendToChild(ContentParent* aContentActor)
{
return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
mMessage, mFlags);
}
nsresult
PushErrorDispatcher::HandleNoChildProcesses()
{
// Report to the console if no content processes are active.
nsCOMPtr<nsIURI> scopeURI;
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
mFlags,
NS_LITERAL_CSTRING("Push"),
nullptr, /* aDocument */
scopeURI, /* aURI */
EmptyString(), /* aLine */
0, /* aLineNumber */
0, /* aColumnNumber */
nsContentUtils::eOMIT_LOCATION);
}
} // namespace dom
} // namespace mozilla

View File

@ -11,9 +11,60 @@
#include "nsIPrincipal.h"
#include "nsString.h"
#include "mozilla/Maybe.h"
namespace mozilla {
namespace dom {
class ContentChild;
class ContentParent;
/**
* `PushDispatcher` is a base class used to forward observer notifications and
* service worker events to the correct process.
*/
class MOZ_STACK_CLASS PushDispatcher
{
public:
// Fires an XPCOM observer notification. This method may be called from both
// processes.
virtual nsresult NotifyObservers() = 0;
// Fires a service worker event. This method is called from the content
// process if e10s is enabled, or the parent otherwise.
virtual nsresult NotifyWorkers() = 0;
// A convenience method that calls `NotifyObservers` and `NotifyWorkers`.
nsresult NotifyObserversAndWorkers();
// Sends an IPDL message to fire an observer notification in the parent
// process. This method is only called from the content process, and only
// if e10s is enabled.
virtual bool SendToParent(ContentChild* aParentActor) = 0;
// Sends an IPDL message to fire an observer notification and a service worker
// event in the content process. This method is only called from the parent,
// and only if e10s is enabled.
virtual bool SendToChild(ContentParent* aContentActor) = 0;
// An optional method, called from the parent if e10s is enabled and there
// are no active content processes. The default behavior is a no-op.
virtual nsresult HandleNoChildProcesses();
protected:
PushDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal);
virtual ~PushDispatcher();
bool ShouldNotifyWorkers();
nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope);
const nsCString mScope;
nsCOMPtr<nsIPrincipal> mPrincipal;
};
/**
* `PushNotifier` implements the `nsIPushNotifier` interface. This service
* broadcasts XPCOM observer notifications for incoming push messages, then
@ -32,14 +83,9 @@ public:
NS_DECL_NSIPUSHNOTIFIER
private:
virtual ~PushNotifier();
~PushNotifier();
nsresult NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData);
nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope);
bool ShouldNotifyWorkers(nsIPrincipal* aPrincipal);
nsresult Dispatch(PushDispatcher& aDispatcher);
};
/**
@ -56,7 +102,7 @@ public:
NS_DECL_NSIPUSHDATA
private:
virtual ~PushData();
~PushData();
nsresult EnsureDecodedText();
@ -79,12 +125,78 @@ public:
NS_DECL_NSIPUSHMESSAGE
private:
virtual ~PushMessage();
~PushMessage();
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIPushData> mData;
};
class PushMessageDispatcher final : public PushDispatcher
{
public:
PushMessageDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aMessageId,
const Maybe<nsTArray<uint8_t>>& aData);
~PushMessageDispatcher();
nsresult NotifyObservers() override;
nsresult NotifyWorkers() override;
bool SendToParent(ContentChild* aParentActor) override;
bool SendToChild(ContentParent* aContentActor) override;
private:
const nsString mMessageId;
const Maybe<nsTArray<uint8_t>> mData;
};
class PushSubscriptionChangeDispatcher final : public PushDispatcher
{
public:
PushSubscriptionChangeDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal);
~PushSubscriptionChangeDispatcher();
nsresult NotifyObservers() override;
nsresult NotifyWorkers() override;
bool SendToParent(ContentChild* aParentActor) override;
bool SendToChild(ContentParent* aContentActor) override;
};
class PushSubscriptionModifiedDispatcher : public PushDispatcher
{
public:
PushSubscriptionModifiedDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal);
~PushSubscriptionModifiedDispatcher();
nsresult NotifyObservers() override;
nsresult NotifyWorkers() override;
bool SendToParent(ContentChild* aParentActor) override;
bool SendToChild(ContentParent* aContentActor) override;
};
class PushErrorDispatcher final : public PushDispatcher
{
public:
PushErrorDispatcher(const nsACString& aScope,
nsIPrincipal* aPrincipal,
const nsAString& aMessage,
uint32_t aFlags);
~PushErrorDispatcher();
nsresult NotifyObservers() override;
nsresult NotifyWorkers() override;
bool SendToParent(ContentChild* aParentActor) override;
bool SendToChild(ContentParent* aContentActor) override;
private:
nsresult HandleNoChildProcesses() override;
const nsString mMessage;
uint32_t mFlags;
};
} // namespace dom
} // namespace mozilla