Backed out 12 changesets (bug 1928702) for causing gv-junit failures on OpenWindowTest#openWindowNullResult CLOSED TREE

Backed out changeset 1bb3c36a15c3 (bug 1928702)
Backed out changeset 984db15bc9e2 (bug 1928702)
Backed out changeset 622a66cdac2e (bug 1928702)
Backed out changeset 33642b1c547b (bug 1928702)
Backed out changeset 56ab49acff35 (bug 1928702)
Backed out changeset f318fe78ff14 (bug 1928702)
Backed out changeset 3daeacd08336 (bug 1928702)
Backed out changeset a497ddb030f6 (bug 1928702)
Backed out changeset 5a94b945a3ad (bug 1928702)
Backed out changeset 984f6e687ff5 (bug 1928702)
Backed out changeset bb63792c3333 (bug 1928702)
Backed out changeset 93937caf6d13 (bug 1928702)
This commit is contained in:
Norisz Fay 2024-11-07 21:28:27 +02:00
parent a9aa58a8e4
commit d0b3b4838f
26 changed files with 420 additions and 950 deletions

View File

@ -113,7 +113,7 @@
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/MediaStatusManager.h"
#include "mozilla/dom/notification/NotificationUtils.h"
#include "mozilla/dom/Notification.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
#include "mozilla/dom/ParentProcessMessageManager.h"
@ -4725,7 +4725,7 @@ mozilla::ipc::IPCResult ContentParent::RecvDisableNotifications(
if (!ValidatePrincipal(aPrincipal)) {
LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
}
Unused << notification::RemovePermission(aPrincipal);
Unused << Notification::RemovePermission(aPrincipal);
return IPC_OK();
}
@ -4738,7 +4738,34 @@ mozilla::ipc::IPCResult ContentParent::RecvOpenNotificationSettings(
if (!ValidatePrincipal(aPrincipal)) {
LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
}
Unused << notification::OpenSettings(aPrincipal);
Unused << Notification::OpenSettings(aPrincipal);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvNotificationEvent(
const nsAString& aType, const NotificationEventData& aData) {
nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::components::ServiceWorkerManager::Service();
if (NS_WARN_IF(!swm)) {
// Probably shouldn't happen, but no need to crash the child process.
return IPC_OK();
}
if (aType.EqualsLiteral("click")) {
nsresult rv = swm->SendNotificationClickEvent(
aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
aData.data(), aData.behavior());
Unused << NS_WARN_IF(NS_FAILED(rv));
} else {
MOZ_ASSERT(aType.EqualsLiteral("close"));
nsresult rv = swm->SendNotificationCloseEvent(
aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
aData.data(), aData.behavior());
Unused << NS_WARN_IF(NS_FAILED(rv));
}
return IPC_OK();
}

View File

@ -1011,6 +1011,9 @@ class ContentParent final : public PContentParent,
mozilla::ipc::IPCResult RecvOpenNotificationSettings(
nsIPrincipal* aPrincipal);
mozilla::ipc::IPCResult RecvNotificationEvent(
const nsAString& aType, const NotificationEventData& aData);
mozilla::ipc::IPCResult RecvLoadURIExternal(
nsIURI* uri, nsIPrincipal* triggeringPrincipal,
nsIPrincipal* redirectPrincipal,

View File

@ -12,7 +12,6 @@ include "mozilla/dom/PermissionMessageUtils.h";
include "mozilla/dom/PropertyBagUtils.h";
include "mozilla/dom/ReferrerInfoUtils.h";
include "mozilla/dom/TabMessageUtils.h";
include "mozilla/dom/notification/IPCUtils.h";
include "mozilla/ipc/URIUtils.h";
include "mozilla/layers/LayersMessageUtils.h";
include "mozilla/net/NeckoMessageUtils.h";
@ -56,8 +55,6 @@ using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingCont
using mozilla::TimeStamp from "mozilla/TimeStamp.h";
[RefCounted] using class mozilla::RemoteLazyInputStream from "mozilla/RemoteLazyInputStream.h";
[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h";
using mozilla::dom::NotificationDirection from "mozilla/dom/NotificationBinding.h";
using mozilla::dom::NotificationBehavior from "mozilla/dom/NotificationBinding.h";
namespace mozilla {
namespace dom {
@ -193,7 +190,7 @@ struct DocShellLoadStateInit
uint32_t LoadType;
uint32_t LoadFlags;
uint32_t InternalLoadFlags;
// The TriggineringSandboxFlags are the SandboxFlags of the entity
// responsible for causing the load to occur.
uint32_t TriggeringSandboxFlags;
@ -332,23 +329,5 @@ struct IPCImage {
ImageIntSize size;
};
// Mostly same as NotificationOptions except:
// * `title` is included (it's a separate parameter in the Notification constructor)
// * `data` is serialized to base64 string by StructuredCloneContainer
// * `vibrate` is normalized to sequence
struct IPCNotificationOptions {
nsString title;
NotificationDirection dir;
nsString lang;
nsString body;
nsString tag;
nsString icon;
bool requireInteraction;
bool silent;
uint32_t[] vibrate;
nsString dataSerialized;
NotificationBehavior behavior;
};
} // namespace dom
} // namespace mozilla

View File

@ -412,6 +412,24 @@ struct IPCURLClassifierFeature
nsCString exceptionHostList;
};
// Transport structure for Notifications API notifications
// (https://developer.mozilla.org/en-US/docs/Web/API/notification) instances
// used exclusively by the NotificationEvent PContent method.
struct NotificationEventData
{
nsCString originSuffix;
nsCString scope;
nsString ID;
nsString title;
nsString dir;
nsString lang;
nsString body;
nsString tag;
nsString icon;
nsString data;
nsString behavior;
};
struct PostMessageData
{
MaybeDiscardedBrowsingContext source;
@ -1149,6 +1167,22 @@ parent:
async AddSecurityState(MaybeDiscardedWindowContext aContext, uint32_t aStateFlags);
// Request that the ServiceWorkerManager in the parent process create a
// notification "click" or "close" event and dispatch it on the relevant
// ServiceWorker. This needs to happen because when a notification is
// created it is tied to a specific content process and when the user clicks
// on the notification, it will be that content process that is notified.
// However, even if the ServiceWorker lives in that process (it may no
// longer be in that process, or may have never lived there), the right/only
// way to talk through the ServiceWorker is through the parent.
//
// This happens on PContent because the ServiceWorkerManager lives on the
// main thread and bouncing this off of PBackground would be silly and
// complex. In the long run, the notification implementation will be
// overhauled to directly process the notification click/close and directly
// translate that to a ServiceWorker event.
async NotificationEvent(nsString type, NotificationEventData data);
// Creates a helper for forwarding data from an nsExternalAppHandler
// running in the content process, to one running in the parent
// process.

View File

@ -1,29 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef DOM_NOTIFICATION_IPCUTILS_H_
#define DOM_NOTIFICATION_IPCUTILS_H_
#include "ipc/EnumSerializer.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/NotificationBinding.h"
namespace IPC {
using NotificationDirection = mozilla::dom::NotificationDirection;
template <>
struct ParamTraits<NotificationDirection>
: public ContiguousEnumSerializerInclusive<NotificationDirection,
NotificationDirection::Auto,
NotificationDirection::Rtl> {};
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::dom::NotificationBehavior, mNoclear,
mNoscreen, mShowOnlyOnce, mSoundFile,
mVibrationPattern);
} // namespace IPC
#endif // DOM_NOTIFICATION_IPCUTILS_H_

View File

@ -30,11 +30,9 @@
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "Navigator.h"
#include "NotificationUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsFocusManager.h"
@ -44,6 +42,7 @@
#include "nsINotificationStorage.h"
#include "nsIPermission.h"
#include "nsIPermissionManager.h"
#include "nsIPushService.h"
#include "nsIScriptError.h"
#include "nsIServiceWorkerManager.h"
#include "nsIUUIDGenerator.h"
@ -54,8 +53,6 @@
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
using namespace mozilla::dom::notification;
namespace mozilla::dom {
using namespace notification;
@ -166,6 +163,11 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
nsCOMPtr<nsINotificationStorage> GetNotificationStorage(bool isPrivate) {
return do_GetService(isPrivate ? NS_MEMORY_NOTIFICATION_STORAGE_CONTRACTID
: NS_NOTIFICATION_STORAGE_CONTRACTID);
}
class NotificationGetRunnable final : public Runnable {
bool mIsPrivate;
const nsString mOrigin;
@ -622,6 +624,8 @@ class NotificationObserver final : public nsIObserver {
protected:
virtual ~NotificationObserver() { AssertIsOnMainThread(); }
nsresult AdjustPushQuota(const char* aTopic);
};
NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
@ -694,7 +698,7 @@ Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
mIsClosed(false),
mIsStored(false),
mTaskCount(0) {
if (!NS_IsMainThread() && mScope.IsEmpty()) {
if (!NS_IsMainThread()) {
mWorkerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(mWorkerPrivate);
mWorkerUseRegularPrincipal = mWorkerPrivate->UseRegularPrincipal();
@ -720,7 +724,24 @@ void Notification::SetAlertName() {
return;
}
ComputeAlertName(GetPrincipal(), mTag, mID, mAlertName);
nsAutoString alertName;
nsresult rv = GetOrigin(GetPrincipal(), alertName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Get the notification name that is unique per origin + tag/ID.
// The name of the alert is of the form origin#tag/ID.
alertName.Append('#');
if (!mTag.IsEmpty()) {
alertName.AppendLiteral("tag:");
alertName.Append(mTag);
} else {
alertName.AppendLiteral("notag:");
alertName.Append(mID);
}
mAlertName = alertName;
}
// May be called on any thread.
@ -740,15 +761,10 @@ already_AddRefed<Notification> Notification::Constructor(
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<Notification> notification =
Create(aGlobal.Context(), global, aTitle, aOptions, u""_ns, aRv);
CreateAndShow(aGlobal.Context(), global, aTitle, aOptions, u""_ns, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
notification->ShowOnMainThread(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (NS_WARN_IF(NS_FAILED(notification->MaybeObserveWindowFrozen()))) {
return nullptr;
}
@ -788,18 +804,35 @@ Notification::ConstructFromFields(
return notification.forget();
}
nsresult Notification::Persist() {
nsresult Notification::PersistNotification() {
AssertIsOnMainThread();
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(IsInPrivateBrowsing());
if (NS_WARN_IF(!notificationStorage)) {
return NS_ERROR_UNEXPECTED;
}
nsString origin;
nsresult rv = GetOrigin(GetPrincipal(), origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsString id;
GetID(id);
nsString alertName;
GetAlertName(alertName);
IPCNotificationOptions options(mTitle, mDir, mLang, mBody, mTag, mIconUrl,
mRequireInteraction, mSilent, mVibrate,
mDataAsBase64, mBehavior);
nsAutoString behavior;
if (!mBehavior.ToJSON(behavior)) {
return NS_ERROR_FAILURE;
}
nsresult rv =
PersistNotification(GetPrincipal(), mID, alertName, options, mScope);
rv = notificationStorage->Put(origin, id, mTitle, GetEnumString(mDir), mLang,
mBody, mTag, mIconUrl, alertName, mDataAsBase64,
behavior, mScope);
if (NS_FAILED(rv)) {
return rv;
@ -809,10 +842,19 @@ nsresult Notification::Persist() {
return NS_OK;
}
void Notification::Unpersist() {
void Notification::UnpersistNotification() {
AssertIsOnMainThread();
if (IsStored()) {
UnpersistNotification(GetPrincipal(), mID);
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(IsInPrivateBrowsing());
if (notificationStorage) {
nsString origin;
nsresult rv = GetOrigin(GetPrincipal(), origin);
if (NS_SUCCEEDED(rv)) {
notificationStorage->Delete(origin, mID);
}
}
SetStoredState(false);
}
}
@ -949,6 +991,50 @@ class WorkerNotificationObserver final : public MainThreadNotificationObserver {
}
};
class ServiceWorkerNotificationObserver final : public nsIObserver {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ServiceWorkerNotificationObserver(
const nsAString& aScope, nsIPrincipal* aPrincipal, const nsAString& aID,
const nsAString& aTitle, NotificationDirection aDir,
const nsAString& aLang, const nsAString& aBody, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aData,
const nsAString& aBehavior)
: mScope(aScope),
mID(aID),
mPrincipal(aPrincipal),
mTitle(aTitle),
mDir(aDir),
mLang(aLang),
mBody(aBody),
mTag(aTag),
mIcon(aIcon),
mData(aData),
mBehavior(aBehavior) {
AssertIsOnMainThread();
MOZ_ASSERT(aPrincipal);
}
private:
~ServiceWorkerNotificationObserver() = default;
const nsString mScope;
const nsString mID;
nsCOMPtr<nsIPrincipal> mPrincipal;
const nsString mTitle;
const NotificationDirection mDir;
const nsString mLang;
const nsString mBody;
const nsString mTag;
const nsString mIcon;
const nsString mData;
const nsString mBehavior;
};
NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver)
bool Notification::DispatchClickEvent() {
AssertIsOnTargetThread();
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
@ -996,35 +1082,47 @@ NotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
if (!strcmp("alertdisablecallback", aTopic)) {
if (XRE_IsParentProcess()) {
return RemovePermission(mPrincipal);
return Notification::RemovePermission(mPrincipal);
}
// Permissions can't be removed from the content process. Send a message
// to the parent; `ContentParent::RecvDisableNotifications` will call
// `RemovePermission`.
ContentChild::GetSingleton()->SendDisableNotifications(mPrincipal);
return NS_OK;
}
if (!strcmp("alertsettingscallback", aTopic)) {
} else if (!strcmp("alertsettingscallback", aTopic)) {
if (XRE_IsParentProcess()) {
return OpenSettings(mPrincipal);
return Notification::OpenSettings(mPrincipal);
}
// `ContentParent::RecvOpenNotificationSettings` notifies observers in the
// parent process.
ContentChild::GetSingleton()->SendOpenNotificationSettings(mPrincipal);
return NS_OK;
}
if (!strcmp("alertshow", aTopic)) {
(void)NS_WARN_IF(NS_FAILED(
AdjustPushQuota(mPrincipal, NotificationStatusChange::Shown)));
}
if (!strcmp("alertfinished", aTopic)) {
(void)NS_WARN_IF(NS_FAILED(
AdjustPushQuota(mPrincipal, NotificationStatusChange::Closed)));
} else if (!strcmp("alertshow", aTopic) || !strcmp("alertfinished", aTopic)) {
Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
}
return mObserver->Observe(aSubject, aTopic, aData);
}
nsresult NotificationObserver::AdjustPushQuota(const char* aTopic) {
nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
do_GetService("@mozilla.org/push/Service;1");
if (!pushQuotaManager) {
return NS_ERROR_FAILURE;
}
nsAutoCString origin;
nsresult rv = mPrincipal->GetOrigin(origin);
if (NS_FAILED(rv)) {
return rv;
}
if (!strcmp("alertshow", aTopic)) {
return pushQuotaManager->NotificationForOriginShown(origin.get());
}
return pushQuotaManager->NotificationForOriginClosed(origin.get());
}
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
// bug 1539845.
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
@ -1048,7 +1146,7 @@ MainThreadNotificationObserver::Observe(nsISupports* aSubject,
nsFocusManager::FocusWindow(outerWindow, CallerType::System);
}
} else if (!strcmp("alertfinished", aTopic)) {
notification->Unpersist();
notification->UnpersistNotification();
notification->mIsClosed = true;
notification->DispatchTrustedEvent(u"close"_ns);
} else if (!strcmp("alertshow", aTopic)) {
@ -1091,7 +1189,7 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
r = new NotificationClickWorkerRunnable(notification, windowHandle);
} else if (!strcmp("alertfinished", aTopic)) {
notification->Unpersist();
notification->UnpersistNotification();
notification->mIsClosed = true;
r = new NotificationEventWorkerRunnable(notification, u"close"_ns);
} else if (!strcmp("alertshow", aTopic)) {
@ -1105,6 +1203,82 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData) {
AssertIsOnMainThread();
nsAutoCString originSuffix;
nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!strcmp("alertclickcallback", aTopic)) {
if (XRE_IsParentProcess()) {
nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::components::ServiceWorkerManager::Service();
if (NS_WARN_IF(!swm)) {
return NS_ERROR_FAILURE;
}
rv = swm->SendNotificationClickEvent(
originSuffix, NS_ConvertUTF16toUTF8(mScope), mID, mTitle,
NS_ConvertASCIItoUTF16(GetEnumString(mDir)), mLang, mBody, mTag,
mIcon, mData, mBehavior);
Unused << NS_WARN_IF(NS_FAILED(rv));
} else {
auto* cc = ContentChild::GetSingleton();
NotificationEventData data(originSuffix, NS_ConvertUTF16toUTF8(mScope),
mID, mTitle,
NS_ConvertASCIItoUTF16(GetEnumString(mDir)),
mLang, mBody, mTag, mIcon, mData, mBehavior);
Unused << cc->SendNotificationEvent(u"click"_ns, data);
}
return NS_OK;
}
if (!strcmp("alertfinished", aTopic)) {
nsString origin;
nsresult rv = Notification::GetOrigin(mPrincipal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Remove closed or dismissed persistent notifications.
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(mPrincipal->GetIsInPrivateBrowsing());
if (notificationStorage) {
notificationStorage->Delete(origin, mID);
}
if (XRE_IsParentProcess()) {
nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::components::ServiceWorkerManager::Service();
if (NS_WARN_IF(!swm)) {
return NS_ERROR_FAILURE;
}
rv = swm->SendNotificationCloseEvent(
originSuffix, NS_ConvertUTF16toUTF8(mScope), mID, mTitle,
NS_ConvertASCIItoUTF16(GetEnumString(mDir)), mLang, mBody, mTag,
mIcon, mData, mBehavior);
Unused << NS_WARN_IF(NS_FAILED(rv));
} else {
auto* cc = ContentChild::GetSingleton();
NotificationEventData data(originSuffix, NS_ConvertUTF16toUTF8(mScope),
mID, mTitle,
NS_ConvertASCIItoUTF16(GetEnumString(mDir)),
mLang, mBody, mTag, mIcon, mData, mBehavior);
Unused << cc->SendNotificationEvent(u"close"_ns, data);
}
return NS_OK;
}
return NS_OK;
}
bool Notification::IsInPrivateBrowsing() {
AssertIsOnMainThread();
@ -1199,23 +1373,39 @@ void Notification::ShowInternal() {
// call the alerts service function.
// XXX(krosylight): Non-persistent notifications probably don't need this
nsresult rv = Persist();
nsresult rv = PersistNotification();
if (NS_FAILED(rv)) {
NS_WARNING("Could not persist Notification");
}
bool isPersistent = false;
nsCOMPtr<nsIObserver> observer;
MOZ_ASSERT(mScope.IsEmpty());
// Ownership passed to observer.
if (mWorkerPrivate) {
// Scope better be set on ServiceWorker initiated requests.
MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
// Keep a pointer so that the feature can tell the observer not to release
// the notification.
mObserver = new WorkerNotificationObserver(std::move(ownership));
observer = mObserver;
if (mScope.IsEmpty()) {
// Ownership passed to observer.
if (mWorkerPrivate) {
// Scope better be set on ServiceWorker initiated requests.
MOZ_ASSERT(!mWorkerPrivate->IsServiceWorker());
// Keep a pointer so that the feature can tell the observer not to release
// the notification.
mObserver = new WorkerNotificationObserver(std::move(ownership));
observer = mObserver;
} else {
observer = new MainThreadNotificationObserver(std::move(ownership));
}
} else {
observer = new MainThreadNotificationObserver(std::move(ownership));
isPersistent = true;
// This observer does not care about the Notification. It will be released
// at the end of this function.
//
// The observer is wholly owned by the NotificationObserver passed to the
// alert service.
nsAutoString behavior;
if (NS_WARN_IF(!mBehavior.ToJSON(behavior))) {
behavior.Truncate();
}
observer = new ServiceWorkerNotificationObserver(
mScope, GetPrincipal(), mID, mTitle, mDir, mLang, mBody, mTag, mIconUrl,
mDataAsBase64, behavior);
}
MOZ_ASSERT(observer);
nsCOMPtr<nsIObserver> alertObserver =
@ -1237,13 +1427,36 @@ void Notification::ShowInternal() {
nsCOMPtr<nsIAlertNotification> alert =
do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
NS_ENSURE_TRUE_VOID(alert);
nsIPrincipal* principal = GetPrincipal();
rv = alert->Init(alertName, mIconUrl, mTitle, mBody, true, uniqueCookie,
NS_ConvertASCIItoUTF16(GetEnumString(mDir)), mLang,
mDataAsBase64, GetPrincipal(), inPrivateBrowsing,
requireInteraction, mSilent, mVibrate);
NS_ENSURE_SUCCESS_VOID(rv);
alertService->ShowAlert(alert, alertObserver);
if (isPersistent) {
JSONStringWriteFunc<nsAutoCString> persistentData;
JSONWriter w(persistentData);
w.Start();
nsAutoString origin;
Notification::GetOrigin(principal, origin);
w.StringProperty("origin", NS_ConvertUTF16toUTF8(origin));
w.StringProperty("id", NS_ConvertUTF16toUTF8(mID));
nsAutoCString originSuffix;
principal->GetOriginSuffix(originSuffix);
w.StringProperty("originSuffix", originSuffix);
w.End();
alertService->ShowPersistentNotification(
NS_ConvertUTF8toUTF16(persistentData.StringCRef()), alert,
alertObserver);
} else {
alertService->ShowAlert(alert, alertObserver);
}
}
/* static */
@ -1552,7 +1765,7 @@ class WorkerGetRunnable final : public Runnable {
return NS_ERROR_UNEXPECTED;
}
nsString origin;
nsresult rv = GetOrigin(principal, origin);
nsresult rv = Notification::GetOrigin(principal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done();
return rv;
@ -1628,15 +1841,28 @@ void Notification::CloseInternal(bool aContextClosed) {
UniquePtr<NotificationRef> ownership;
std::swap(ownership, mTempRef);
if (mIsClosed) {
return;
SetAlertName();
UnpersistNotification();
if (!mIsClosed) {
nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service();
if (alertService) {
nsAutoString alertName;
GetAlertName(alertName);
alertService->CloseAlert(alertName, aContextClosed);
}
}
}
nsresult Notification::GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) {
if (!aPrincipal) {
return NS_ERROR_FAILURE;
}
nsAutoString alertName;
GetAlertName(alertName);
UnregisterNotification(
GetPrincipal(), mID, alertName,
aContextClosed ? CloseMode::InactiveGlobal : CloseMode::CloseMethod);
nsresult rv =
nsContentUtils::GetWebExposedOriginSerialization(aPrincipal, aOrigin);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
bool Notification::RequireInteraction() const { return mRequireInteraction; }
@ -1933,28 +2159,44 @@ already_AddRefed<Promise> Notification::ShowPersistentNotification(
return nullptr;
}
// We check permission here rather than pass the Promise to NotificationTask
// which leads to uglier code.
// XXX: GetPermission is a synchronous blocking function on workers.
NotificationPermission permission =
GetPermission(aGlobal, PermissionCheckPurpose::NotificationShow, aRv);
// Step 6.1: If the result of getting the notifications permission state is
// not "granted", then queue a global task on the DOM manipulation task source
// given global to reject promise with a TypeError, and abort these steps.
if (NS_WARN_IF(aRv.Failed()) ||
permission != NotificationPermission::Granted) {
p->MaybeRejectWithTypeError("Permission to show Notification denied.");
return p.forget();
}
// "Otherwise, resolve promise with undefined."
// The Notification may still not be shown due to other errors, but the spec
// is not concerned with those.
p->MaybeResolveWithUndefined();
// Step 5: Let notification be the result of creating a notification given
// title, options, thiss relevant settings object, and
// serviceWorkerRegistration. If this threw an exception, then reject promise
// with that exception and return promise.
// XXX: We create Notification object almost solely to share the parameter
// normalization steps. It would be nice to export that and skip creating
// object here.
//
// XXX: This should happen before the permission check per the spec, as this
// can throw errors too. This should be split into create and show.
RefPtr<Notification> notification =
Create(aCx, aGlobal, aTitle, aOptions, aScope, aRv);
CreateAndShow(aCx, aGlobal, aTitle, aOptions, aScope, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (!notification->CreateActor(p) || !notification->SendShow(p)) {
return nullptr;
}
return p.forget();
}
/* static */
already_AddRefed<Notification> Notification::Create(
already_AddRefed<Notification> Notification::CreateAndShow(
JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aTitle,
const NotificationOptions& aOptions, const nsAString& aScope,
ErrorResult& aRv) {
@ -1975,99 +2217,48 @@ already_AddRefed<Notification> Notification::Create(
notification->SetScope(aScope);
return notification.forget();
}
void Notification::ShowOnMainThread(ErrorResult& aRv) {
auto ref = MakeUnique<NotificationRef>(this);
auto ref = MakeUnique<NotificationRef>(notification);
if (NS_WARN_IF(!ref->Initialized())) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return;
return nullptr;
}
// Queue a task to show the notification.
nsCOMPtr<nsIRunnable> showNotificationTask = new NotificationTask(
"Notification::CreateAndShow", std::move(ref), NotificationTask::eShow);
nsresult rv = DispatchToMainThread(showNotificationTask.forget());
nsresult rv =
notification->DispatchToMainThread(showNotificationTask.forget());
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchTrustedEvent(u"error"_ns);
notification->DispatchTrustedEvent(u"error"_ns);
}
return notification.forget();
}
bool Notification::CreateActor(Promise* aPromise) {
mozilla::ipc::PBackgroundChild* backgroundActor =
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
IPCNotificationOptions options(mTitle, mDir, mLang, mBody, mTag, mIconUrl,
mRequireInteraction, mSilent, mVibrate,
mDataAsBase64, mBehavior);
// Note: We are not using the typical PBackground managed actor here as we
// want the actor to be in the main thread of the main process. Instead we
// pass the endpoint to PBackground, it dispatches a runnable to the main
// thread, and the endpoint is bound there.
mozilla::ipc::Endpoint<notification::PNotificationParent> parentEndpoint;
mozilla::ipc::Endpoint<notification::PNotificationChild> childEndpoint;
notification::PNotification::CreateEndpoints(&parentEndpoint, &childEndpoint);
mActor = new notification::NotificationChild();
if (!childEndpoint.Bind(mActor)) {
return false;
/* static */
nsresult Notification::RemovePermission(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIPermissionManager> permissionManager =
mozilla::components::PermissionManager::Service();
if (!permissionManager) {
return NS_ERROR_FAILURE;
}
nsIPrincipal* principal;
nsIPrincipal* effectiveStoragePrincipal;
bool isSecureContext;
// TODO: Should get nsIGlobalObject methods for each method
if (WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate()) {
principal = workerPrivate->GetPrincipal();
effectiveStoragePrincipal = workerPrivate->GetEffectiveStoragePrincipal();
isSecureContext = workerPrivate->IsSecureContext();
} else {
nsGlobalWindowInner* win = GetOwnerWindow();
NS_ENSURE_TRUE(win, false);
principal = win->GetPrincipal();
effectiveStoragePrincipal = win->GetEffectiveStoragePrincipal();
isSecureContext = win->IsSecureContext();
}
(void)backgroundActor->SendCreateNotificationParent(
std::move(parentEndpoint), WrapNotNull(principal),
WrapNotNull(effectiveStoragePrincipal), isSecureContext, mID, mScope,
options);
return true;
permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification"_ns);
return NS_OK;
}
bool Notification::SendShow(Promise* aPromise) {
mActor->SendShow()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr{this}, promise = RefPtr(aPromise)](
notification::PNotificationChild::ShowPromise::ResolveOrRejectValue&&
aResult) {
if (aResult.IsReject()) {
promise->MaybeRejectWithUnknownError("Failed to open notification");
if (self->mActor) {
self->mActor->Close();
}
return;
}
CopyableErrorResult rv = aResult.ResolveValue();
if (rv.Failed()) {
promise->MaybeReject(std::move(rv));
if (self->mActor) {
self->mActor->Close();
}
return;
}
promise->MaybeResolveWithUndefined();
});
return true;
/* static */
nsresult Notification::OpenSettings(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_FAILURE;
}
// Notify other observers so they can show settings UI.
obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
return NS_OK;
}
void Notification::DisconnectFromOwner() {

View File

@ -11,7 +11,6 @@
#include "mozilla/GlobalFreezeObserver.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/NotificationBinding.h"
#include "mozilla/dom/notification/NotificationChild.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/quota/QuotaCommon.h"
@ -226,6 +225,9 @@ class Notification : public DOMEventTargetHelper, public GlobalFreezeObserver {
bool DispatchClickEvent();
static nsresult RemovePermission(nsIPrincipal* aPrincipal);
static nsresult OpenSettings(nsIPrincipal* aPrincipal);
nsresult DispatchToMainThread(already_AddRefed<nsIRunnable>&& aRunnable);
protected:
@ -254,6 +256,8 @@ class Notification : public DOMEventTargetHelper, public GlobalFreezeObserver {
nsPIDOMWindowInner* aWindow,
notification::PermissionCheckPurpose aPurpose, ErrorResult& rv);
static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
void GetAlertName(nsAString& aRetval) {
AssertIsOnMainThread();
if (mAlertName.IsEmpty()) {
@ -269,8 +273,6 @@ class Notification : public DOMEventTargetHelper, public GlobalFreezeObserver {
mScope = aScope;
}
WeakPtr<notification::NotificationChild> mActor;
const nsString mID;
const nsString mTitle;
const nsString mBody;
@ -311,19 +313,15 @@ class Notification : public DOMEventTargetHelper, public GlobalFreezeObserver {
//
// Note that aCx may not be in the compartment of aGlobal, but aOptions will
// have its JS things in the compartment of aCx.
static already_AddRefed<Notification> Create(
static already_AddRefed<Notification> CreateAndShow(
JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aTitle,
const NotificationOptions& aOptions, const nsAString& aScope,
ErrorResult& aRv);
void ShowOnMainThread(ErrorResult& aRv);
bool CreateActor(Promise* aPromise);
bool SendShow(Promise* aPromise);
nsIPrincipal* GetPrincipal();
nsresult Persist();
void Unpersist();
nsresult PersistNotification();
void UnpersistNotification();
void SetAlertName();

View File

@ -1,25 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef DOM_NOTIFICATION_NOTIFICATIONCHILD_H_
#define DOM_NOTIFICATION_NOTIFICATIONCHILD_H_
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/notification/PNotificationChild.h"
namespace mozilla::dom::notification {
class NotificationChild final : public PNotificationChild,
public SupportsWeakPtr {
NS_INLINE_DECL_REFCOUNTING(NotificationChild)
private:
~NotificationChild() = default;
};
} // namespace mozilla::dom::notification
#endif // DOM_NOTIFICATION_NOTIFICATIONCHILD_H_

View File

@ -1,265 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "NotificationParent.h"
#include "nsThreadUtils.h"
#include "NotificationUtils.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/Components.h"
#include "nsComponentManagerUtils.h"
#include "nsIAlertsService.h"
#include "nsIServiceWorkerManager.h"
namespace mozilla::dom::notification {
NS_IMPL_ISUPPORTS(NotificationParent, nsIObserver)
NS_IMETHODIMP
NotificationParent::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!strcmp("alertdisablecallback", aTopic)) {
return RemovePermission(mPrincipal);
}
if (!strcmp("alertsettingscallback", aTopic)) {
return OpenSettings(mPrincipal);
}
if (!strcmp("alertclickcallback", aTopic)) {
return FireClickEvent();
}
if (!strcmp("alertshow", aTopic)) {
(void)NS_WARN_IF(NS_FAILED(
AdjustPushQuota(mPrincipal, NotificationStatusChange::Shown)));
if (!mResolver) {
#ifdef ANDROID
// XXX: This can happen as we resolve showNotification() immediately on
// Android for now and a mock service may still call this.
return NS_OK;
#else
MOZ_ASSERT_UNREACHABLE("Are we getting double show events?");
return NS_ERROR_FAILURE;
#endif
}
mResolver.take().value()(CopyableErrorResult());
return NS_OK;
}
if (!strcmp("alertfinished", aTopic)) {
(void)NS_WARN_IF(NS_FAILED(
AdjustPushQuota(mPrincipal, NotificationStatusChange::Closed)));
if (mResolver) {
// alertshow happens first before alertfinished, and it should have If
// not it means it failed to show and is bailing out.
// XXX: Apparently XUL manual do not disturb mode does this without firing
// alertshow at all.
mResolver.take().value()(CopyableErrorResult(NS_ERROR_FAILURE));
return NS_OK;
}
// XXX: QM_TRY?
(void)NS_WARN_IF(NS_FAILED(UnpersistNotification(mPrincipal, mId)));
(void)NS_WARN_IF(NS_FAILED(FireCloseEvent()));
// Unpersisted already and being unregistered already by nsIAlertsService
mDangling = true;
Close();
return NS_OK;
}
MOZ_ASSERT_UNREACHABLE("Unknown notification topic");
return NS_OK;
}
nsresult NotificationParent::FireClickEvent() {
// This needs to be done here rather than in the child actor's
// RecvNotifyClick because the caller might not be in the service worker
// context but in the window context
if (nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::components::ServiceWorkerManager::Service()) {
nsAutoCString originSuffix;
MOZ_TRY(mPrincipal->GetOriginSuffix(originSuffix));
nsAutoString behavior;
if (!mOptions.behavior().ToJSON(behavior)) {
return NS_ERROR_FAILURE;
}
MOZ_TRY(swm->SendNotificationClickEvent(
originSuffix, NS_ConvertUTF16toUTF8(mScope), mId, mOptions.title(),
NS_ConvertASCIItoUTF16(GetEnumString(mOptions.dir())), mOptions.lang(),
mOptions.body(), mOptions.tag(), mOptions.icon(),
mOptions.dataSerialized(), behavior));
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsresult NotificationParent::FireCloseEvent() {
// This needs to be done here rather than in the child actor's
// RecvNotifyClose because the caller might not be in the service worker
// context but in the window context
if (nsCOMPtr<nsIServiceWorkerManager> swm =
mozilla::components::ServiceWorkerManager::Service()) {
nsAutoCString originSuffix;
MOZ_TRY(mPrincipal->GetOriginSuffix(originSuffix));
nsAutoString behavior;
if (!mOptions.behavior().ToJSON(behavior)) {
return NS_ERROR_FAILURE;
}
MOZ_TRY(swm->SendNotificationCloseEvent(
originSuffix, NS_ConvertUTF16toUTF8(mScope), mId, mOptions.title(),
NS_ConvertASCIItoUTF16(GetEnumString(mOptions.dir())), mOptions.lang(),
mOptions.body(), mOptions.tag(), mOptions.icon(),
mOptions.dataSerialized(), behavior));
return NS_OK;
}
return NS_ERROR_FAILURE;
}
// Step 4 of
// https://notifications.spec.whatwg.org/#dom-notification-notification
mozilla::ipc::IPCResult NotificationParent::RecvShow(ShowResolver&& aResolver) {
mResolver.emplace(std::move(aResolver));
// Step 4.1: If the result of getting the notifications permission state is
// not "granted", then queue a task to fire an event named error on this, and
// abort these steps.
NotificationPermission permission = GetNotificationPermission(
mPrincipal, mEffectiveStoragePrincipal, mIsSecureContext,
PermissionCheckPurpose::NotificationShow);
if (permission != NotificationPermission::Granted) {
CopyableErrorResult rv;
rv.ThrowTypeError("Permission to show Notification denied.");
mResolver.take().value()(rv);
mDangling = true;
return IPC_OK();
}
// Step 4.2: Run the fetch steps for notification. (Will happen in
// nsIAlertNotification::LoadImage)
// Step 4.3: Run the show steps for notification.
nsresult rv = Show();
if (NS_FAILED(rv)) {
mResolver.take().value()(CopyableErrorResult(rv));
}
// If not failed, the resolver will be called asynchronously by
// NotificationObserver
return IPC_OK();
}
nsresult NotificationParent::Show() {
// Step 4.3 the show steps, which are almost all about processing `tag` and
// then displaying the notification. Both are handled by
// nsIAlertsService::ShowAlert/PersistentNotification. The below is all about
// constructing the observer (for show and close events) right and ultimately
// call the alerts service function.
// XXX(krosylight): Non-persistent notifications probably don't need this
nsAutoString alertName;
GetAlertName(alertName);
nsresult rv =
PersistNotification(mPrincipal, mId, alertName, mOptions, mScope);
if (NS_FAILED(rv)) {
NS_WARNING("Could not persist Notification");
}
// In the case of IPC, the parent process uses the cookie to map to
// nsIObserver. Thus the cookie must be unique to differentiate observers.
// XXX(krosylight): This is about ContentChild::mAlertObserver which is not
// useful when called by the parent process. This should be removed when we
// make nsIAlertsService parent process only.
nsString obsoleteCookie = u"notification:"_ns;
bool requireInteraction = mOptions.requireInteraction();
if (!StaticPrefs::dom_webnotifications_requireinteraction_enabled()) {
requireInteraction = false;
}
nsCOMPtr<nsIAlertNotification> alert =
do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
if (!alert) {
return NS_ERROR_NOT_AVAILABLE;
}
MOZ_TRY(alert->Init(alertName, mOptions.icon(), mOptions.title(),
mOptions.body(), true, obsoleteCookie,
NS_ConvertASCIItoUTF16(GetEnumString(mOptions.dir())),
mOptions.lang(), mOptions.dataSerialized(), mPrincipal,
mPrincipal->GetIsInPrivateBrowsing(), requireInteraction,
mOptions.silent(), mOptions.vibrate()));
nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service();
JSONStringWriteFunc<nsAutoCString> persistentData;
JSONWriter w(persistentData);
w.Start();
nsAutoString origin;
GetOrigin(mPrincipal, origin);
w.StringProperty("origin", NS_ConvertUTF16toUTF8(origin));
w.StringProperty("id", NS_ConvertUTF16toUTF8(mId));
nsAutoCString originSuffix;
mPrincipal->GetOriginSuffix(originSuffix);
w.StringProperty("originSuffix", originSuffix);
w.End();
MOZ_TRY(alertService->ShowPersistentNotification(
NS_ConvertUTF8toUTF16(persistentData.StringCRef()), alert, this));
#ifdef ANDROID
// XXX: the Android nsIAlertsService is broken and doesn't send alertshow
// properly, which means we cannot depend on it to resolve the promise. For
// now we resolve the promise here.
// (This now fires onshow event regardless of the actual result, but it should
// be better than the previous behavior that did not do anything at all)
mResolver.take().value()(CopyableErrorResult());
#endif
return NS_OK;
}
nsresult NotificationParent::BindToMainThread(
Endpoint<PNotificationParent>&& aParentEndpoint,
PBackgroundParent::CreateNotificationParentResolver&& aResolver) {
nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"NotificationParent::BindToMainThread",
[self = RefPtr(this), endpoint = std::move(aParentEndpoint),
resolver = std::move(aResolver), thread]() mutable {
bool result = endpoint.Bind(self);
thread->Dispatch(NS_NewRunnableFunction(
"NotificationParent::BindToMainThreadResult",
[result, resolver = std::move(resolver)]() { resolver(result); }));
}));
return NS_OK;
}
void NotificationParent::ActorDestroy(ActorDestroyReason aWhy) {
if (mDangling) {
// We had no permission, so nothing to clean up.
return;
}
nsAutoString alertName;
GetAlertName(alertName);
UnregisterNotification(mPrincipal, mId, alertName, CloseMode::InactiveGlobal);
}
void NotificationParent::MaybeInitAlertName() {
if (!mAlertName.IsEmpty()) {
return;
}
ComputeAlertName(mPrincipal, mOptions.tag(), mId, mAlertName);
}
} // namespace mozilla::dom::notification

View File

@ -1,81 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef DOM_NOTIFICATION_NOTIFICATIONPARENT_H_
#define DOM_NOTIFICATION_NOTIFICATIONPARENT_H_
#include "mozilla/dom/notification/PNotificationParent.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/dom/DOMTypes.h"
namespace mozilla::dom::notification {
class NotificationParent final : public PNotificationParent,
public nsIObserver {
using IPCResult = mozilla::ipc::IPCResult;
public:
// Threadsafe to pass the class from PBackground to main thread
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
NotificationParent(NotNull<nsIPrincipal*> aPrincipal,
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal,
bool aIsSecureContext, const nsAString& aId,
const nsAString& aScope,
const IPCNotificationOptions& aOptions)
: mPrincipal(aPrincipal),
mEffectiveStoragePrincipal(aEffectiveStoragePrincipal),
mIsSecureContext(aIsSecureContext),
mId(aId),
mScope(aScope),
mOptions(aOptions) {
MOZ_ASSERT(!mScope.IsEmpty(), "Only for persistent notifications for now");
};
IPCResult RecvShow(ShowResolver&& aResolver);
nsresult BindToMainThread(
Endpoint<PNotificationParent>&& aParentEndpoint,
PBackgroundParent::CreateNotificationParentResolver&& aResolver);
void ActorDestroy(ActorDestroyReason aWhy) override;
private:
~NotificationParent() = default;
nsresult Show();
nsresult FireClickEvent();
nsresult FireCloseEvent();
void GetAlertName(nsAString& aRetval) {
if (mAlertName.IsEmpty()) {
MaybeInitAlertName();
}
aRetval = mAlertName;
}
void MaybeInitAlertName();
Maybe<NotificationParent::ShowResolver> mResolver;
NotNull<nsCOMPtr<nsIPrincipal>> mPrincipal;
NotNull<nsCOMPtr<nsIPrincipal>> mEffectiveStoragePrincipal;
bool mIsSecureContext;
nsString mId;
nsString mScope;
IPCNotificationOptions mOptions;
nsString mAlertName;
// Whether it's now a dangling actor without corresponding OS notification,
// either because it's closed or denied permission. We don't have to call
// CloseAlert if this is the case.
bool mDangling = false;
};
} // namespace mozilla::dom::notification
#endif // DOM_NOTIFICATION_NOTIFICATIONPARENT_H_

View File

@ -9,15 +9,9 @@
#include "mozilla/BasePrincipal.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/NotificationBinding.h"
#include "mozilla/glean/GleanMetrics.h"
#include "nsContentUtils.h"
#include "nsIAlertsService.h"
#include "nsINotificationStorage.h"
#include "nsIPermissionManager.h"
#include "nsIPushService.h"
#include "nsServiceManagerUtils.h"
namespace mozilla::dom::notification {
@ -148,138 +142,4 @@ NotificationPermission GetNotificationPermission(
return GetRawNotificationPermission(aPrincipal);
}
nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin) {
if (!aPrincipal) {
return NS_ERROR_FAILURE;
}
MOZ_TRY(
nsContentUtils::GetWebExposedOriginSerialization(aPrincipal, aOrigin));
return NS_OK;
}
void ComputeAlertName(nsIPrincipal* aPrincipal, const nsString& aTag,
const nsString& aId, nsString& aResult) {
nsAutoString alertName;
nsresult rv = GetOrigin(aPrincipal, alertName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Get the notification name that is unique per origin + tag/ID.
// The name of the alert is of the form origin#tag/ID.
alertName.Append('#');
if (!aTag.IsEmpty()) {
alertName.AppendLiteral("tag:");
alertName.Append(aTag);
} else {
alertName.AppendLiteral("notag:");
alertName.Append(aId);
}
aResult = alertName;
}
nsCOMPtr<nsINotificationStorage> GetNotificationStorage(bool isPrivate) {
return do_GetService(isPrivate ? NS_MEMORY_NOTIFICATION_STORAGE_CONTRACTID
: NS_NOTIFICATION_STORAGE_CONTRACTID);
}
nsresult PersistNotification(nsIPrincipal* aPrincipal, const nsString& aId,
const nsString& aAlertName,
const IPCNotificationOptions& aOptions,
const nsString& aScope) {
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(aPrincipal->GetIsInPrivateBrowsing());
if (NS_WARN_IF(!notificationStorage)) {
return NS_ERROR_UNEXPECTED;
}
nsString origin;
nsresult rv = GetOrigin(aPrincipal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString behavior;
if (!aOptions.behavior().ToJSON(behavior)) {
return NS_ERROR_FAILURE;
}
rv = notificationStorage->Put(
origin, aId, aOptions.title(), GetEnumString(aOptions.dir()),
aOptions.lang(), aOptions.body(), aOptions.tag(), aOptions.icon(),
aAlertName, aOptions.dataSerialized(), behavior, aScope);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
nsresult UnpersistNotification(nsIPrincipal* aPrincipal, const nsString& aId) {
if (!aPrincipal) {
return NS_ERROR_FAILURE;
}
if (nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(aPrincipal->GetIsInPrivateBrowsing())) {
nsString origin;
MOZ_TRY(GetOrigin(aPrincipal, origin));
return notificationStorage->Delete(origin, aId);
}
return NS_ERROR_FAILURE;
}
void UnregisterNotification(nsIPrincipal* aPrincipal, const nsString& aId,
const nsString& aAlertName, CloseMode aCloseMode) {
// XXX: unpersist only when explicitly closed, bug 1095073
UnpersistNotification(aPrincipal, aId);
if (nsCOMPtr<nsIAlertsService> alertService = components::Alerts::Service()) {
alertService->CloseAlert(
aAlertName,
/* aContextClosed */ aCloseMode == CloseMode::InactiveGlobal);
}
}
nsresult RemovePermission(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIPermissionManager> permissionManager =
mozilla::components::PermissionManager::Service();
if (!permissionManager) {
return NS_ERROR_FAILURE;
}
permissionManager->RemoveFromPrincipal(aPrincipal, "desktop-notification"_ns);
return NS_OK;
}
nsresult OpenSettings(nsIPrincipal* aPrincipal) {
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) {
return NS_ERROR_FAILURE;
}
// Notify other observers so they can show settings UI.
obs->NotifyObservers(aPrincipal, "notifications-open-settings", nullptr);
return NS_OK;
}
nsresult AdjustPushQuota(nsIPrincipal* aPrincipal,
NotificationStatusChange aChange) {
nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
do_GetService("@mozilla.org/push/Service;1");
if (!pushQuotaManager) {
return NS_ERROR_FAILURE;
}
nsAutoCString origin;
MOZ_TRY(aPrincipal->GetOrigin(origin));
if (aChange == NotificationStatusChange::Shown) {
return pushQuotaManager->NotificationForOriginShown(origin.get());
}
return pushQuotaManager->NotificationForOriginClosed(origin.get());
}
} // namespace mozilla::dom::notification

View File

@ -8,21 +8,14 @@
#define DOM_NOTIFICATION_NOTIFICATIONUTILS_H_
#include <cstdint>
#include "nsCOMPtr.h"
#include "nsStringFwd.h"
enum class nsresult : uint32_t;
class nsIPrincipal;
class nsINotificationStorage;
namespace mozilla::dom {
enum class NotificationPermission : uint8_t;
class Document;
} // namespace mozilla::dom
namespace mozilla::dom {
class IPCNotificationOptions;
}
namespace mozilla::dom::notification {
/**
@ -63,34 +56,6 @@ NotificationPermission GetNotificationPermission(
nsIPrincipal* aPrincipal, nsIPrincipal* aEffectiveStoragePrincipal,
bool isSecureContext, PermissionCheckPurpose aPurpose);
nsCOMPtr<nsINotificationStorage> GetNotificationStorage(bool isPrivate);
nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
void ComputeAlertName(nsIPrincipal* aPrincipal, const nsString& aTag,
const nsString& aId, nsString& aResult);
nsresult PersistNotification(nsIPrincipal* aPrincipal, const nsString& aId,
const nsString& aAlertName,
const IPCNotificationOptions& aOptions,
const nsString& aScope);
nsresult UnpersistNotification(nsIPrincipal* aPrincipal, const nsString& aId);
enum class CloseMode {
CloseMethod,
// Either on global teardown or freeze
InactiveGlobal,
};
void UnregisterNotification(nsIPrincipal* aPrincipal, const nsString& aId,
const nsString& aAlertName, CloseMode aCloseMode);
nsresult RemovePermission(nsIPrincipal* aPrincipal);
nsresult OpenSettings(nsIPrincipal* aPrincipal);
enum class NotificationStatusChange { Shown, Closed };
nsresult AdjustPushQuota(nsIPrincipal* aPrincipal,
NotificationStatusChange aChange);
} // namespace mozilla::dom::notification
#endif // DOM_NOTIFICATION_NOTIFICATIONUTILS_H_

View File

@ -1,23 +0,0 @@
/* 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 protocol PBackground;
include "ipc/ErrorIPCUtils.h";
using mozilla::CopyableErrorResult from "mozilla/ErrorResult.h";
namespace mozilla {
namespace dom {
namespace notification {
[ChildProc=anydom]
async protocol PNotification {
parent:
async Show() returns (CopyableErrorResult rv);
};
} // namespace notification
} // namespace dom
} // namespace mozilla

View File

@ -20,24 +20,12 @@ EXPORTS.mozilla.dom += [
"NotificationEvent.h",
]
EXPORTS.mozilla.dom.notification += [
"IPCUtils.h",
"NotificationChild.h",
"NotificationParent.h",
"NotificationUtils.h", # Exported for ContentParent for now
]
UNIFIED_SOURCES += [
"Notification.cpp",
"NotificationEvent.cpp",
"NotificationParent.cpp",
"NotificationUtils.cpp",
]
IPDL_SOURCES += [
"PNotification.ipdl",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul"

View File

@ -13,11 +13,6 @@ const GleanTest = new Proxy(
Services.fog.testResetFOG();
});
},
async flush() {
return SpecialPowers.spawnChrome([], async () => {
await Services.fog.testFlushAllChildren();
});
},
},
{
get(gleanTestObj, gleanTestProp) {

View File

@ -69,12 +69,6 @@ support-files = [
]
tags = "openwindow"
["test_notification_serviceworker_show.html"]
skip-if = ["xorigin"] # Bug 1792790
support-files = [
"notification_show_sw.js",
]
["test_notification_worker_child.html"]
skip-if = [
"os == 'android'", # Bug 1816427, Notification.onshow/close are broken on Android
@ -84,11 +78,6 @@ support-files = [
"notification_worker_child-parent.js",
]
["test_notification_worker_click.html"]
skip-if = [
"os == 'android'", # Bug 1816427, Notification.onshow/close are broken on Android
]
["test_notification_worker_permission.html"]
support-files = ["notification_permission_worker.js"]

View File

@ -1,13 +0,0 @@
onmessage = async ev => {
if (ev.data !== "show") {
return;
}
const shown = await self.registration.showNotification("title").then(
() => true,
() => false
);
const clients = await self.clients.matchAll({ includeUncontrolled: true });
for (let client of clients) {
client.postMessage({ shown });
}
};

View File

@ -45,7 +45,7 @@
await new Promise(r => new Notification("first party").onerror = r);
const showCount = await GleanTest.webNotification.showOrigin.first_party.testGetValue();
is(showCount, 1, "Notification first party show attempt counter should increment once.");
is(showCount, 1, "Notification first party request permission counter should increment once.");
},
async function(done) {

View File

@ -49,7 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
});
const requestCount = await GleanTest.webNotification.requestPermissionOrigin.nested_first_party.testGetValue();
is(requestCount, 1, "Notification nested first party request permission counter should increment once.");
is(requestCount, 1, "Notification third party request permission counter should increment once.");
await SpecialPowers.spawn(iframe, [], async () => {
const nested = this.content.document.querySelector("iframe");
@ -59,7 +59,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
});
const permissionCount = await GleanTest.webNotification.permissionOrigin.nested_first_party.testGetValue();
is(permissionCount, 1, "Notification nested first party permission read counter should increment once.");
is(permissionCount, 1, "Notification third party permission read counter should increment once.");
await SpecialPowers.spawn(iframe, [], async () => {
const nested = this.content.document.querySelector("iframe");
@ -69,7 +69,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
});
const showCount = await GleanTest.webNotification.showOrigin.nested_first_party.testGetValue();
is(showCount, 1, "Notification nested first party show attempt counter should increment once.");
is(showCount, 1, "Notification third party show attempt counter should increment once.");
SimpleTest.finish();
})();

View File

@ -3,10 +3,9 @@
<head>
<title>ServiceWorkerRegistration.getNotifications() on main thread and worker thread.</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="MockAlertsService.js"></script>
<script src="NotificationTest.js"></script>
<script src="GleanTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="/tests/dom/notification/test/mochitest/MockAlertsService.js"></script>
<script type="text/javascript" src="/tests/dom/notification/test/mochitest/NotificationTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
@ -44,15 +43,10 @@
}
async function testDismiss() {
await GleanTest.testResetFOG();
// Dismissed persistent notifications should be removed from the
// notification list.
const reg = await navigator.serviceWorker.getRegistration("./notification/");
await reg.showNotification("This is a notification that will be closed", { tag: "dismiss" });
const showCount = await GleanTest.webNotification.showOrigin.first_party.testGetValue();
is(showCount, 1, "Notification first party show attempt counter should increment once.");
const notifications = await reg.getNotifications();
is(notifications.length, 1, "There should be one visible notification");
is(notifications[0].tag, "dismiss", "Tag should match");

View File

@ -1,47 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test showNotification called within service worker</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="NotificationTest.js"></script>
<script src="GleanTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script>
add_task(async function test() {
await GleanTest.testResetFOG();
info("Registering service worker");
let swr = await navigator.serviceWorker.register("notification_show_sw.js");
await navigator.serviceWorker.ready;
SimpleTest.registerCleanupFunction(async () => {
await swr.unregister();
navigator.serviceWorker.onmessage = null;
});
info("Showing notification");
await NotificationTest.allowNotifications();
let messagePromise = new Promise(r => navigator.serviceWorker.addEventListener("message", r, { once: true }));
swr.active.postMessage("show");
ok((await messagePromise).data.shown);
await GleanTest.flush();
let permissionCount = await GleanTest.webNotification.showOrigin.first_party.testGetValue();
is(permissionCount, 1, "Notification first party show attempt counter should increment once.");
info("Denying notification");
await NotificationTest.denyNotifications();
messagePromise = new Promise(r => navigator.serviceWorker.addEventListener("message", r, { once: true }));
swr.active.postMessage("show");
ok(!(await messagePromise).data.shown);
await GleanTest.flush();
permissionCount = await GleanTest.webNotification.showOrigin.first_party.testGetValue();
is(permissionCount, 2, "Notification first party show attempt counter should increment once more.");
});
</script>
</pre>
</body>
</html>

View File

@ -1,28 +0,0 @@
<!DOCTYPE HTML>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/dom/notification/test/mochitest/MockAlertsService.js"></script>
<script src="/tests/dom/notification/test/mochitest/NotificationTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<script>
add_task(async function test_notification_worker_click() {
await NotificationTest.allowNotifications();
await MockAlertsService.register();
await MockAlertsService.enableAutoClick();
var w = new Worker(URL.createObjectURL(new Blob([`
new Notification("click").onclick = () => {
postMessage("clicked");
};
`])));
await new Promise(resolve => {
w.onmessage = function(e) {
if (e.data === 'clicked') {
resolve();
}
}
});
ok(true, "notification click event happened from worker");
});
</script>

View File

@ -54,7 +54,6 @@
#include "mozilla/dom/locks/LockManagerParent.h"
#include "mozilla/dom/localstorage/ActorsParent.h"
#include "mozilla/dom/network/UDPSocketParent.h"
#include "mozilla/dom/notification/NotificationParent.h"
#include "mozilla/dom/quota/ActorsParent.h"
#include "mozilla/dom/quota/QuotaParent.h"
#include "mozilla/dom/simpledb/ActorsParent.h"
@ -480,23 +479,6 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateWebTransportParent(
return IPC_OK();
}
mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateNotificationParent(
Endpoint<dom::notification::PNotificationParent>&& aParentEndpoint,
NotNull<nsIPrincipal*> aPrincipal,
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal,
const bool& aIsSecureContext, const nsAString& aId, const nsAString& aScope,
const IPCNotificationOptions& aOptions,
CreateNotificationParentResolver&& aResolver) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
auto actor = MakeRefPtr<dom::notification::NotificationParent>(
aPrincipal, aEffectiveStoragePrincipal, aIsSecureContext, aId, aScope,
aOptions);
actor->BindToMainThread(std::move(aParentEndpoint), std::move(aResolver));
return IPC_OK();
}
already_AddRefed<PIdleSchedulerParent>
BackgroundParentImpl::AllocPIdleSchedulerParent() {
AssertIsOnBackgroundThread();
@ -1399,7 +1381,8 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvPLockManagerConstructor(
already_AddRefed<dom::locks::PLockManagerParent>
BackgroundParentImpl::AllocPLockManagerParent(NotNull<nsIPrincipal*> aPrincipal,
const Maybe<nsID>& aClientId) {
return MakeAndAddRef<dom::locks::LockManagerParent>(aPrincipal, aClientId);
return MakeAndAddRef<mozilla::dom::locks::LockManagerParent>(aPrincipal,
aClientId);
}
already_AddRefed<dom::PFetchParent> BackgroundParentImpl::AllocPFetchParent() {

View File

@ -129,14 +129,6 @@ class BackgroundParentImpl : public PBackgroundParent {
Endpoint<PWebTransportParent>&& aParentEndpoint,
CreateWebTransportParentResolver&& aResolver) override;
mozilla::ipc::IPCResult RecvCreateNotificationParent(
Endpoint<dom::notification::PNotificationParent>&& aParentEndpoint,
NotNull<nsIPrincipal*> aPrincipal,
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal,
const bool& aIsSecureContext, const nsAString& aId,
const nsAString& aScope, const IPCNotificationOptions& aOptions,
CreateNotificationParentResolver&& aResolver) final;
already_AddRefed<PIdleSchedulerParent> AllocPIdleSchedulerParent() override;
PTemporaryIPCBlobParent* AllocPTemporaryIPCBlobParent() override;

View File

@ -48,7 +48,6 @@ include protocol PVsync;
include protocol PRemoteDecoderManager;
include protocol PWebTransport;
include protocol PFetch;
include protocol PNotification;
include ClientIPCTypes;
include DOMTypes;
@ -187,21 +186,6 @@ parent:
Endpoint<PWebTransportParent> aParentEndpoint)
returns(nsresult rv, uint8_t aReliability); // Actually WebTransportReliabityMode enum
// XXX(krosylight): This should ultimately use nsID instead of scope, see bug 1881812.
/**
* Finish the setup of a new PNotification top level protocol.
*/
async CreateNotificationParent(
Endpoint<PNotificationParent> aParentEndpoint,
nsIPrincipal aPrincipal,
nsIPrincipal aEffectiveStoragePrincipal,
bool aIsSecureContext,
nsString aId,
nsString aScope,
IPCNotificationOptions aOptions
) returns (bool rv);
async PVsync();
async PCameras();

View File

@ -1,7 +1,6 @@
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/helpers.js
// (Cannot use `global=serviceworker` because testdriver only supports window)
@ -15,8 +14,8 @@ navigator.serviceWorker.addEventListener("message", async ev => {
}
});
promise_setup(async () => {
await trySettingPermission("granted");
promise_setup(() => {
return test_driver.set_permission({ name: "notifications" }, "granted");
});
service_worker_test("getnotifications-sw.js", "Service worker test setup");