Bug 1951379 - Part 1: Use IPCNotification as a member in Notification r=asuth

This also modifies InitFromJSVal and InitFromBase64 into static functions, to allow mIPCNotification to be const.

Differential Revision: https://phabricator.services.mozilla.com/D240169
This commit is contained in:
Kagami Sascha Rosylight
2025-03-04 18:00:16 +00:00
parent 43e8b7b8e6
commit e88a50df09
6 changed files with 107 additions and 123 deletions

View File

@@ -286,23 +286,10 @@ bool Notification::PrefEnabled(JSContext* aCx, JSObject* aObj) {
return StaticPrefs::dom_webnotifications_enabled();
}
Notification::Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
const nsAString& aTitle, const nsAString& aBody,
NotificationDirection aDir, const nsAString& aLang,
const nsAString& aTag, const nsAString& aIconUrl,
bool aRequireInteraction, bool aSilent,
nsTArray<uint32_t>&& aVibrate)
Notification::Notification(nsIGlobalObject* aGlobal,
IPCNotification&& aIPCNotification)
: DOMEventTargetHelper(aGlobal),
mID(aID),
mTitle(aTitle),
mBody(aBody),
mDir(aDir),
mLang(aLang),
mTag(aTag),
mIconUrl(aIconUrl),
mRequireInteraction(aRequireInteraction),
mSilent(aSilent),
mVibrate(std::move(aVibrate)),
mIPCNotification(std::move(aIPCNotification)),
mData(JS::NullValue()) {
KeepAliveIfHasListenersFor(nsGkAtoms::onclick);
KeepAliveIfHasListenersFor(nsGkAtoms::onshow);
@@ -357,14 +344,33 @@ already_AddRefed<Notification> Notification::Constructor(
return notification.forget();
}
// NOTE(krosylight): Maybe move this check to the parent process?
Result<Ok, nsresult> ValidateBase64Data(const nsAString& aData) {
if (aData.IsEmpty()) {
return Ok();
}
// To and from to ensure it is valid base64.
RefPtr<nsStructuredCloneContainer> container =
new nsStructuredCloneContainer();
MOZ_TRY(container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION));
nsString result;
MOZ_TRY(container->GetDataAsBase64(result));
return Ok();
}
// static
Result<already_AddRefed<Notification>, QMResult> Notification::ConstructFromIPC(
Result<already_AddRefed<Notification>, nsresult> Notification::ConstructFromIPC(
nsIGlobalObject* aGlobal, const IPCNotification& aIPCNotification,
const nsAString& aServiceWorkerRegistrationScope) {
MOZ_ASSERT(aGlobal);
const IPCNotificationOptions& ipcOptions = aIPCNotification.options();
MOZ_TRY(ValidateBase64Data(ipcOptions.dataSerialized()));
RootedDictionary<NotificationOptions> options(RootingCx());
options.mDir = ipcOptions.dir();
options.mLang = ipcOptions.lang();
@@ -372,14 +378,13 @@ Result<already_AddRefed<Notification>, QMResult> Notification::ConstructFromIPC(
options.mTag = ipcOptions.tag();
options.mIcon = ipcOptions.icon();
IgnoredErrorResult rv;
RefPtr<Notification> notification = CreateInternal(
aGlobal, aIPCNotification.id(), ipcOptions.title(), options, rv);
RefPtr<Notification> notification =
CreateInternal(aGlobal, aIPCNotification.id(), ipcOptions.title(),
ipcOptions.dataSerialized(), options, rv);
if (NS_WARN_IF(rv.Failed())) {
return Err(ToQMResult(NS_ERROR_FAILURE));
return Err(NS_ERROR_FAILURE);
}
QM_TRY(notification->InitFromBase64(ipcOptions.dataSerialized()));
notification->SetScope(aServiceWorkerRegistrationScope);
return notification.forget();
@@ -396,8 +401,9 @@ void Notification::MaybeNotifyClose() {
// https://notifications.spec.whatwg.org/#create-a-notification
already_AddRefed<Notification> Notification::CreateInternal(
nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
const NotificationOptions& aOptions, ErrorResult& aRv) {
// Step 20: Set notifications silent preference to options["silent"].
const nsAString& aDataSerialized, const NotificationOptions& aOptions,
ErrorResult& aRv) {
// Step 17: Set notifications silent preference to options["silent"].
bool silent = false;
if (StaticPrefs::dom_webnotifications_silent_enabled()) {
silent = aOptions.mSilent;
@@ -406,7 +412,7 @@ already_AddRefed<Notification> Notification::CreateInternal(
nsTArray<uint32_t> vibrate;
if (StaticPrefs::dom_webnotifications_vibrate_enabled() &&
aOptions.mVibrate.WasPassed()) {
// Step 4: If options["silent"] is true and options["vibrate"] exists, then
// Step 2: If options["silent"] is true and options["vibrate"] exists, then
// throw a TypeError.
if (silent) {
aRv.ThrowTypeError(
@@ -414,7 +420,7 @@ already_AddRefed<Notification> Notification::CreateInternal(
return nullptr;
}
// Step 17: If options["vibrate"] exists, then validate and normalize it and
// Step 14: If options["vibrate"] exists, then validate and normalize it and
// set notifications vibration pattern to the return value.
const OwningUnsignedLongOrUnsignedLongSequence& value =
aOptions.mVibrate.Value();
@@ -427,16 +433,22 @@ already_AddRefed<Notification> Notification::CreateInternal(
}
}
// Step 15: If options["icon"] exists, then parse it using baseURL, and if
// Step 12: If options["icon"] exists, then parse it using baseURL, and if
// that does not return failure, set notifications icon URL to the return
// value. (Otherwise icon URL is not set.)
nsString iconUrl = aOptions.mIcon;
ResolveIconURL(aGlobal, iconUrl);
RefPtr<Notification> notification = new Notification(
aGlobal, aID, aTitle, aOptions.mBody, aOptions.mDir, aOptions.mLang,
aOptions.mTag, iconUrl, aOptions.mRequireInteraction, silent,
std::move(vibrate));
IPCNotification ipcNotification(
nsString(aID),
IPCNotificationOptions(nsString(aTitle), aOptions.mDir,
nsString(aOptions.mLang), nsString(aOptions.mBody),
nsString(aOptions.mTag), iconUrl,
aOptions.mRequireInteraction, silent, vibrate,
nsString(aDataSerialized)));
RefPtr<Notification> notification =
new Notification(aGlobal, std::move(ipcNotification));
return notification.forget();
}
@@ -668,21 +680,26 @@ void Notification::Close() {
}
}
bool Notification::RequireInteraction() const { return mRequireInteraction; }
bool Notification::RequireInteraction() const {
return mIPCNotification.options().requireInteraction();
}
bool Notification::Silent() const { return mSilent; }
bool Notification::Silent() const {
return mIPCNotification.options().silent();
}
void Notification::GetVibrate(nsTArray<uint32_t>& aRetval) const {
aRetval = mVibrate.Clone();
aRetval = mIPCNotification.options().vibrate().Clone();
}
void Notification::GetData(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval) {
if (mData.isNull() && !mDataAsBase64.IsEmpty()) {
const nsString& dataSerialized = mIPCNotification.options().dataSerialized();
if (mData.isNull() && !dataSerialized.IsEmpty()) {
nsresult rv;
RefPtr<nsStructuredCloneContainer> container =
new nsStructuredCloneContainer();
rv = container->InitFromBase64(mDataAsBase64, JS_STRUCTURED_CLONE_VERSION);
rv = container->InitFromBase64(dataSerialized, JS_STRUCTURED_CLONE_VERSION);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRetval.setNull();
return;
@@ -708,39 +725,19 @@ void Notification::GetData(JSContext* aCx,
aRetval.set(mData);
}
void Notification::InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
ErrorResult& aRv) {
if (!mDataAsBase64.IsEmpty() || aData.isNull()) {
return;
static Result<nsString, nsresult> SerializeDataAsBase64(
JSContext* aCx, JS::Handle<JS::Value> aData) {
if (aData.isNull()) {
return nsString();
}
RefPtr<nsStructuredCloneContainer> dataObjectContainer =
new nsStructuredCloneContainer();
aRv = dataObjectContainer->InitFromJSVal(aData, aCx);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_TRY(dataObjectContainer->InitFromJSVal(aData, aCx));
aRv = dataObjectContainer->GetDataAsBase64(mDataAsBase64);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
nsString result;
MOZ_TRY(dataObjectContainer->GetDataAsBase64(result));
Result<Ok, QMResult> Notification::InitFromBase64(const nsAString& aData) {
MOZ_ASSERT(mDataAsBase64.IsEmpty());
if (aData.IsEmpty()) {
// No data; skipping
return Ok();
}
// To and fro to ensure it is valid base64.
RefPtr<nsStructuredCloneContainer> container =
new nsStructuredCloneContainer();
QM_TRY(QM_TO_RESULT(
container->InitFromBase64(aData, JS_STRUCTURED_CLONE_VERSION)));
QM_TRY(QM_TO_RESULT(container->GetDataAsBase64(mDataAsBase64)));
return Ok();
return result;
}
// Steps 2-5 of
@@ -795,22 +792,25 @@ already_AddRefed<Promise> Notification::ShowPersistentNotification(
}
/* static */
// https://notifications.spec.whatwg.org/#create-a-notification
already_AddRefed<Notification> Notification::Create(
JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aTitle,
const NotificationOptions& aOptions, const nsAString& aScope,
ErrorResult& aRv) {
MOZ_ASSERT(aGlobal);
RefPtr<Notification> notification =
CreateInternal(aGlobal, u""_ns, aTitle, aOptions, aRv);
if (aRv.Failed()) {
// Step 4: Set notifications data to
// StructuredSerializeForStorage(options["data"]).
JS::Rooted<JS::Value> data(aCx, aOptions.mData);
Result<nsString, nsresult> dataResult = SerializeDataAsBase64(aCx, data);
if (dataResult.isErr()) {
aRv = dataResult.unwrapErr();
return nullptr;
}
// Make a structured clone of the aOptions.mData object
JS::Rooted<JS::Value> data(aCx, aOptions.mData);
notification->InitFromJSVal(aCx, data, aRv);
if (NS_WARN_IF(aRv.Failed())) {
RefPtr<Notification> notification = CreateInternal(
aGlobal, u""_ns, aTitle, dataResult.unwrap(), aOptions, aRv);
if (aRv.Failed()) {
return nullptr;
}
@@ -822,9 +822,6 @@ already_AddRefed<Notification> Notification::Create(
bool Notification::CreateActor() {
mozilla::ipc::PBackgroundChild* backgroundActor =
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
IPCNotificationOptions options(mTitle, mDir, mLang, mBody, mTag, mIconUrl,
mRequireInteraction, mSilent, mVibrate,
mDataAsBase64);
// 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
@@ -866,8 +863,8 @@ bool Notification::CreateActor() {
(void)backgroundActor->SendCreateNotificationParent(
std::move(parentEndpoint), WrapNotNull(principal),
WrapNotNull(effectiveStoragePrincipal), isSecureContext, mID, mScope,
options);
WrapNotNull(effectiveStoragePrincipal), isSecureContext, mScope,
mIPCNotification);
return true;
}

View File

@@ -77,23 +77,33 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
/**
* Used when retrieving notification objects from the parent process.
*/
static Result<already_AddRefed<Notification>, QMResult> ConstructFromIPC(
static Result<already_AddRefed<Notification>, nsresult> ConstructFromIPC(
nsIGlobalObject* aGlobal, const IPCNotification& aIPCNotification,
const nsAString& aServiceWorkerRegistrationScope);
void GetID(nsAString& aRetval) { aRetval = mID; }
void GetID(nsAString& aRetval) { aRetval = mIPCNotification.id(); }
void GetTitle(nsAString& aRetval) { aRetval = mTitle; }
void GetTitle(nsAString& aRetval) {
aRetval = mIPCNotification.options().title();
}
NotificationDirection Dir() { return mDir; }
NotificationDirection Dir() { return mIPCNotification.options().dir(); }
void GetLang(nsAString& aRetval) { aRetval = mLang; }
void GetLang(nsAString& aRetval) {
aRetval = mIPCNotification.options().lang();
}
void GetBody(nsAString& aRetval) { aRetval = mBody; }
void GetBody(nsAString& aRetval) {
aRetval = mIPCNotification.options().body();
}
void GetTag(nsAString& aRetval) { aRetval = mTag; }
void GetTag(nsAString& aRetval) {
aRetval = mIPCNotification.options().tag();
}
void GetIcon(nsAString& aRetval) { aRetval = mIconUrl; }
void GetIcon(nsAString& aRetval) {
aRetval = mIPCNotification.options().icon();
}
void MaybeNotifyClose();
@@ -133,11 +143,6 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
ErrorResult& aRv);
Result<Ok, QMResult> InitFromBase64(const nsAString& aData);
static NotificationPermission GetPermission(
nsIGlobalObject* aGlobal, notification::PermissionCheckPurpose aPurpose,
ErrorResult& aRv);
@@ -147,16 +152,12 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
nsresult DispatchToMainThread(already_AddRefed<nsIRunnable>&& aRunnable);
protected:
Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
const nsAString& aTitle, const nsAString& aBody,
NotificationDirection aDir, const nsAString& aLang,
const nsAString& aTag, const nsAString& aIconUrl,
bool aRequireInteraction, bool aSilent,
nsTArray<uint32_t>&& aVibrate);
Notification(nsIGlobalObject* aGlobal, IPCNotification&& aIPCNotification);
static already_AddRefed<Notification> CreateInternal(
nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
const NotificationOptions& aOptions, ErrorResult& aRv);
const nsAString& aDataSerialized, const NotificationOptions& aOptions,
ErrorResult& aRv);
void Deactivate();
@@ -171,19 +172,7 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
WeakPtr<notification::NotificationChild> mActor;
// An existing ID loaded from NotificationDB. Leave it empty if we are
// creating a new notification.
const nsString mID;
const nsString mTitle;
const nsString mBody;
const NotificationDirection mDir;
const nsString mLang;
const nsString mTag;
const nsString mIconUrl;
const bool mRequireInteraction;
const bool mSilent;
nsTArray<uint32_t> mVibrate;
nsString mDataAsBase64;
const IPCNotification mIPCNotification;
// It's null until GetData is first called
JS::Heap<JS::Value> mData;

View File

@@ -26,15 +26,14 @@ class NotificationParent final : public PNotificationParent,
NotificationParent(NotNull<nsIPrincipal*> aPrincipal,
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal,
bool aIsSecureContext, const nsAString& aId,
const nsAString& aScope,
const IPCNotificationOptions& aOptions)
bool aIsSecureContext, const nsAString& aScope,
const IPCNotification& aNotification)
: mPrincipal(aPrincipal),
mEffectiveStoragePrincipal(aEffectiveStoragePrincipal),
mIsSecureContext(aIsSecureContext),
mId(aId),
mId(aNotification.id()),
mScope(aScope),
mOptions(aOptions) {};
mOptions(aNotification.options()) {};
IPCResult RecvShow(ShowResolver&& aResolver);
IPCResult RecvClose();
@@ -64,7 +63,7 @@ class NotificationParent final : public PNotificationParent,
// stay empty if the function fails.
nsString mId;
nsString mScope;
IPCNotificationOptions mOptions;
const IPCNotificationOptions mOptions;
nsString mAlertName;

View File

@@ -484,15 +484,15 @@ 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,
const bool& aIsSecureContext, const nsAString& aScope,
const IPCNotification& aNotification,
CreateNotificationParentResolver&& aResolver) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
auto actor = MakeRefPtr<dom::notification::NotificationParent>(
aPrincipal, aEffectiveStoragePrincipal, aIsSecureContext, aId, aScope,
aOptions);
aPrincipal, aEffectiveStoragePrincipal, aIsSecureContext, aScope,
aNotification);
actor->BindToMainThread(std::move(aParentEndpoint), std::move(aResolver));
return IPC_OK();
}

View File

@@ -133,8 +133,8 @@ class BackgroundParentImpl : public PBackgroundParent {
Endpoint<dom::notification::PNotificationParent>&& aParentEndpoint,
NotNull<nsIPrincipal*> aPrincipal,
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal,
const bool& aIsSecureContext, const nsAString& aId,
const nsAString& aScope, const IPCNotificationOptions& aOptions,
const bool& aIsSecureContext, const nsAString& aScope,
const IPCNotification& aNotification,
CreateNotificationParentResolver&& aResolver) final;
already_AddRefed<PIdleSchedulerParent> AllocPIdleSchedulerParent() override;

View File

@@ -196,9 +196,8 @@ parent:
nsIPrincipal aPrincipal,
nsIPrincipal aEffectiveStoragePrincipal,
bool aIsSecureContext,
nsString aId,
nsString aScope,
IPCNotificationOptions aOptions
IPCNotification aNotification
) returns (bool rv);
async PVsync();