mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 03:05:34 +00:00
21fdc3d97d
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
543 lines
16 KiB
C++
543 lines
16 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "PushNotifier.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsICategoryManager.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsXPCOM.h"
|
|
#include "ServiceWorkerManager.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/unused.h"
|
|
|
|
#include "mozilla/dom/BodyUtil.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
using workers::AssertIsOnMainThread;
|
|
using workers::ServiceWorkerManager;
|
|
|
|
PushNotifier::PushNotifier()
|
|
{}
|
|
|
|
PushNotifier::~PushNotifier()
|
|
{}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_0(PushNotifier)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushNotifier)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushNotifier)
|
|
NS_INTERFACE_MAP_ENTRY(nsIPushNotifier)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushNotifier)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushNotifier)
|
|
|
|
NS_IMETHODIMP
|
|
PushNotifier::NotifyPushWithData(const nsACString& aScope,
|
|
nsIPrincipal* aPrincipal,
|
|
const nsAString& aMessageId,
|
|
uint32_t aDataLen, uint8_t* aData)
|
|
{
|
|
nsTArray<uint8_t> data;
|
|
if (!data.SetCapacity(aDataLen, fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if (!data.InsertElementsAt(0, aData, aDataLen, fallible)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(data));
|
|
return Dispatch(dispatcher);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
|
|
const nsAString& aMessageId)
|
|
{
|
|
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
|
|
return Dispatch(dispatcher);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
|
|
nsIPrincipal* aPrincipal)
|
|
{
|
|
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
|
|
return Dispatch(dispatcher);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
|
|
nsIPrincipal* aPrincipal)
|
|
{
|
|
PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
|
|
return Dispatch(dispatcher);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
|
|
const nsAString& aMessage, uint32_t aFlags)
|
|
{
|
|
PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
|
|
return Dispatch(dispatcher);
|
|
}
|
|
|
|
nsresult
|
|
PushNotifier::Dispatch(PushDispatcher& aDispatcher)
|
|
{
|
|
if (XRE_IsParentProcess()) {
|
|
// Always notify XPCOM observers in the parent process.
|
|
Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
|
|
|
|
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]));
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
if (BrowserTabsRemoteAutostart()) {
|
|
// e10s is enabled, but no content processes are active.
|
|
return aDispatcher.HandleNoChildProcesses();
|
|
}
|
|
|
|
// e10s is disabled; notify workers in the parent.
|
|
return aDispatcher.NotifyWorkers();
|
|
}
|
|
|
|
// 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 rv = aDispatcher.NotifyObserversAndWorkers();
|
|
|
|
ContentChild* parentActor = ContentChild::GetSingleton();
|
|
if (!NS_WARN_IF(!parentActor)) {
|
|
Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
PushData::PushData(const nsTArray<uint8_t>& aData)
|
|
: mData(aData)
|
|
{}
|
|
|
|
PushData::~PushData()
|
|
{}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_0(PushData)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushData)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushData)
|
|
NS_INTERFACE_MAP_ENTRY(nsIPushData)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushData)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushData)
|
|
|
|
nsresult
|
|
PushData::EnsureDecodedText()
|
|
{
|
|
if (mData.IsEmpty() || !mDecodedText.IsEmpty()) {
|
|
return NS_OK;
|
|
}
|
|
nsresult rv = BodyUtil::ConsumeText(
|
|
mData.Length(),
|
|
reinterpret_cast<uint8_t*>(mData.Elements()),
|
|
mDecodedText
|
|
);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
mDecodedText.Truncate();
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushData::Text(nsAString& aText)
|
|
{
|
|
nsresult rv = EnsureDecodedText();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
aText = mDecodedText;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushData::Json(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aResult)
|
|
{
|
|
nsresult rv = EnsureDecodedText();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
ErrorResult error;
|
|
BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error);
|
|
return error.StealNSResult();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushData::Binary(uint32_t* aDataLen, uint8_t** aData)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aDataLen);
|
|
NS_ENSURE_ARG_POINTER(aData);
|
|
|
|
*aData = nullptr;
|
|
if (mData.IsEmpty()) {
|
|
*aDataLen = 0;
|
|
return NS_OK;
|
|
}
|
|
uint32_t length = mData.Length();
|
|
uint8_t* data = static_cast<uint8_t*>(NS_Alloc(length * sizeof(uint8_t)));
|
|
if (!data) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
memcpy(data, mData.Elements(), length * sizeof(uint8_t));
|
|
*aDataLen = length;
|
|
*aData = data;
|
|
return NS_OK;
|
|
}
|
|
|
|
PushMessage::PushMessage(nsIPrincipal* aPrincipal, nsIPushData* aData)
|
|
: mPrincipal(aPrincipal)
|
|
, mData(aData)
|
|
{}
|
|
|
|
PushMessage::~PushMessage()
|
|
{}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(PushMessage, mPrincipal, mData)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessage)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushMessage)
|
|
NS_INTERFACE_MAP_ENTRY(nsIPushMessage)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessage)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessage)
|
|
|
|
NS_IMETHODIMP
|
|
PushMessage::GetPrincipal(nsIPrincipal** aPrincipal)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aPrincipal);
|
|
|
|
nsCOMPtr<nsIPrincipal> principal = mPrincipal;
|
|
principal.forget(aPrincipal);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
PushMessage::GetData(nsIPushData** aData)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aData);
|
|
|
|
nsCOMPtr<nsIPushData> data = mData;
|
|
data.forget(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* aContentActor)
|
|
{
|
|
return aContentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
|
|
IPC::Principal(mPrincipal));
|
|
}
|
|
|
|
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
|