Bug 1950117 - Add PServiceWorkerRegistration::GetNotifications r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D239793
This commit is contained in:
Kagami Sascha Rosylight
2025-03-03 16:48:49 +00:00
parent 7b8e2b9f73
commit 517e37a9da
12 changed files with 197 additions and 380 deletions

View File

@@ -359,5 +359,10 @@ struct IPCNotification {
IPCNotificationOptions options;
};
union IPCNotificationsOrError {
IPCNotification[];
nsresult;
};
} // namespace dom
} // namespace mozilla

View File

@@ -70,122 +70,6 @@ struct NotificationStrings {
const nsString mServiceWorkerRegistrationScope;
};
class GetCallbackBase : public nsINotificationStorageCallback {
public:
NS_IMETHOD Handle(const nsAString& aID, const nsAString& aTitle,
const nsAString& aDir, const nsAString& aLang,
const nsAString& aBody, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aData,
const nsAString& aServiceWorkerRegistrationScope) final {
AssertIsOnMainThread();
MOZ_ASSERT(!aID.IsEmpty());
NotificationStrings strings = {
nsString(aID),
nsString(aTitle),
nsString(aDir),
nsString(aLang),
nsString(aBody),
nsString(aTag),
nsString(aIcon),
nsString(aData),
nsString(aServiceWorkerRegistrationScope),
};
mStrings.AppendElement(std::move(strings));
return NS_OK;
}
NS_IMETHOD Done() override = 0;
protected:
virtual ~GetCallbackBase() = default;
nsTArray<NotificationStrings> mStrings;
};
class NotificationStorageCallback final : public GetCallbackBase {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(NotificationStorageCallback)
NotificationStorageCallback(nsIGlobalObject* aWindow, Promise* aPromise)
: mWindow(aWindow), mPromise(aPromise) {
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aPromise);
}
NS_IMETHOD Done() final {
nsTArray<RefPtr<Notification>> notifications(mStrings.Length());
for (const NotificationStrings& strings : mStrings) {
auto result = Notification::ConstructFromFields(
mWindow, strings.mID, strings.mTitle, strings.mDir, strings.mLang,
strings.mBody, strings.mTag, strings.mIcon, strings.mData,
strings.mServiceWorkerRegistrationScope);
if (result.isErr()) {
continue;
}
RefPtr<Notification> n = result.unwrap();
notifications.AppendElement(n.forget());
}
mPromise->MaybeResolve(notifications);
return NS_OK;
}
private:
virtual ~NotificationStorageCallback() = default;
nsCOMPtr<nsIGlobalObject> mWindow;
RefPtr<Promise> mPromise;
const nsString mScope;
};
NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationStorageCallback)
NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationStorageCallback)
NS_IMPL_CYCLE_COLLECTION(NotificationStorageCallback, mWindow, mPromise);
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationStorageCallback)
NS_INTERFACE_MAP_ENTRY(nsINotificationStorageCallback)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
class NotificationGetRunnable final : public Runnable {
bool mIsPrivate;
const nsString mOrigin;
const nsString mScope;
const nsString mTag;
nsCOMPtr<nsINotificationStorageCallback> mCallback;
public:
NotificationGetRunnable(const nsAString& aOrigin, const nsAString& aScope,
const nsAString& aTag,
nsINotificationStorageCallback* aCallback,
bool aIsPrivate)
: Runnable("NotificationGetRunnable"),
mIsPrivate(aIsPrivate),
mOrigin(aOrigin),
mScope(aScope),
mTag(aTag),
mCallback(aCallback) {}
NS_IMETHOD
Run() override {
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(mIsPrivate);
if (NS_WARN_IF(!notificationStorage)) {
return NS_ERROR_UNEXPECTED;
}
nsresult rv = notificationStorage->Get(mOrigin, mScope, mTag, mCallback);
// XXXnsm Is it guaranteed mCallback will be called in case of failure?
Unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
};
class NotificationPermissionRequest : public ContentPermissionRequestBase,
public nsIRunnable,
public nsINamed {
@@ -473,36 +357,6 @@ already_AddRefed<Notification> Notification::Constructor(
return notification.forget();
}
// static
Result<already_AddRefed<Notification>, QMResult>
Notification::ConstructFromFields(
nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
const nsAString& aServiceWorkerRegistrationScope) {
MOZ_ASSERT(aGlobal);
RootedDictionary<NotificationOptions> options(RootingCx());
options.mDir = StringToEnum<NotificationDirection>(aDir).valueOr(
NotificationDirection::Auto);
options.mLang = aLang;
options.mBody = aBody;
options.mTag = aTag;
options.mIcon = aIcon;
IgnoredErrorResult rv;
RefPtr<Notification> notification =
CreateInternal(aGlobal, aID, aTitle, options, rv);
if (NS_WARN_IF(rv.Failed())) {
return Err(ToQMResult(NS_ERROR_FAILURE));
}
QM_TRY(notification->InitFromBase64(aData));
notification->SetScope(aServiceWorkerRegistrationScope);
return notification.forget();
}
// static
Result<already_AddRefed<Notification>, QMResult> Notification::ConstructFromIPC(
nsIGlobalObject* aGlobal, const IPCNotification& aIPCNotification,
@@ -797,200 +651,6 @@ nsresult Notification::ResolveIconURL(nsIGlobalObject* aGlobal,
return rv;
}
already_AddRefed<Promise> Notification::Get(
nsPIDOMWindowInner* aWindow, const GetNotificationOptions& aFilter,
const nsAString& aScope, ErrorResult& aRv) {
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
if (!doc) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsString origin;
aRv = GetOrigin(doc->NodePrincipal(), origin);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<Promise> promise = Promise::Create(aWindow->AsGlobal(), aRv);
if (aRv.Failed()) {
return nullptr;
}
nsCOMPtr<nsINotificationStorageCallback> callback =
new NotificationStorageCallback(aWindow->AsGlobal(), promise);
RefPtr<NotificationGetRunnable> r = new NotificationGetRunnable(
origin, aScope, aFilter.mTag, callback, doc->IsInPrivateBrowsing());
aRv = aWindow->AsGlobal()->Dispatch(r.forget());
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
}
class WorkerGetResultRunnable final : public NotificationWorkerRunnable {
RefPtr<PromiseWorkerProxy> mPromiseProxy;
const nsTArray<NotificationStrings> mStrings;
public:
WorkerGetResultRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aPromiseProxy,
nsTArray<NotificationStrings>&& aStrings)
: NotificationWorkerRunnable(aWorkerPrivate, "WorkerGetResultRunnable"),
mPromiseProxy(aPromiseProxy),
mStrings(std::move(aStrings)) {}
void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override {
RefPtr<Promise> workerPromise = mPromiseProxy->GetWorkerPromise();
// Once Worker had already started shutdown, workerPromise would be nullptr
if (!workerPromise) {
return;
}
nsTArray<RefPtr<Notification>> notifications(mStrings.Length());
for (const NotificationStrings& strings : mStrings) {
auto result = Notification::ConstructFromFields(
aWorkerPrivate->GlobalScope(), strings.mID, strings.mTitle,
strings.mDir, strings.mLang, strings.mBody, strings.mTag,
strings.mIcon, strings.mData,
/* strings.mBehavior, not
* supported */
strings.mServiceWorkerRegistrationScope);
if (result.isErr()) {
continue;
}
RefPtr<Notification> n = result.unwrap();
notifications.AppendElement(n.forget());
}
workerPromise->MaybeResolve(notifications);
mPromiseProxy->CleanUp();
}
};
class WorkerGetCallback final : public GetCallbackBase {
RefPtr<PromiseWorkerProxy> mPromiseProxy;
public:
NS_DECL_ISUPPORTS
explicit WorkerGetCallback(PromiseWorkerProxy* aProxy)
: mPromiseProxy(aProxy) {
AssertIsOnMainThread();
MOZ_ASSERT(aProxy);
}
NS_IMETHOD Done() final {
AssertIsOnMainThread();
MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
RefPtr<PromiseWorkerProxy> proxy = std::move(mPromiseProxy);
MutexAutoLock lock(proxy->Lock());
if (proxy->CleanedUp()) {
return NS_OK;
}
RefPtr<WorkerGetResultRunnable> r = new WorkerGetResultRunnable(
proxy->GetWorkerPrivate(), proxy, std::move(mStrings));
r->Dispatch(proxy->GetWorkerPrivate());
return NS_OK;
}
private:
~WorkerGetCallback() = default;
};
NS_IMPL_ISUPPORTS(WorkerGetCallback, nsINotificationStorageCallback)
class WorkerGetRunnable final : public Runnable {
RefPtr<PromiseWorkerProxy> mPromiseProxy;
const nsString mTag;
const nsString mScope;
public:
WorkerGetRunnable(PromiseWorkerProxy* aProxy, const nsAString& aTag,
const nsAString& aScope)
: Runnable("WorkerGetRunnable"),
mPromiseProxy(aProxy),
mTag(aTag),
mScope(aScope) {
MOZ_ASSERT(mPromiseProxy);
}
NS_IMETHOD
Run() override {
AssertIsOnMainThread();
MutexAutoLock lock(mPromiseProxy->Lock());
if (mPromiseProxy->CleanedUp()) {
return NS_OK;
}
auto* principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
auto isPrivate = principal->GetIsInPrivateBrowsing();
nsCOMPtr<nsINotificationStorageCallback> callback =
new WorkerGetCallback(mPromiseProxy);
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(isPrivate);
if (NS_WARN_IF(!notificationStorage)) {
callback->Done();
return NS_ERROR_UNEXPECTED;
}
nsString origin;
nsresult rv = GetOrigin(principal, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done();
return rv;
}
rv = notificationStorage->Get(origin, mScope, mTag, callback);
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->Done();
return rv;
}
return NS_OK;
}
private:
~WorkerGetRunnable() = default;
};
// static
already_AddRefed<Promise> Notification::WorkerGet(
WorkerPrivate* aWorkerPrivate, const GetNotificationOptions& aFilter,
const nsAString& aScope, ErrorResult& aRv) {
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<Promise> p = Promise::Create(aWorkerPrivate->GlobalScope(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<PromiseWorkerProxy> proxy =
PromiseWorkerProxy::Create(aWorkerPrivate, p);
if (!proxy) {
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
return nullptr;
}
RefPtr<WorkerGetRunnable> r =
new WorkerGetRunnable(proxy, aFilter.mTag, aScope);
// Since this is called from script via
// ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
return p.forget();
}
JSObject* Notification::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return mozilla::dom::Notification_Binding::Wrap(aCx, this, aGivenProto);

View File

@@ -74,22 +74,6 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
const GlobalObject& aGlobal, const nsAString& aTitle,
const NotificationOptions& aOption, ErrorResult& aRv);
/**
* Used when dispatching the ServiceWorkerEvent.
*
* Does not initialize the Notification's behavior.
* This is because:
* 1) The Notification is not shown to the user and so the behavior
* parameters don't matter.
* 2) The default binding requires main thread for parsing the JSON from the
* string behavior.
*/
static Result<already_AddRefed<Notification>, QMResult> ConstructFromFields(
nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
const nsAString& aServiceWorkerRegistrationScope);
/**
* Used when retrieving notification objects from the parent process.
*/
@@ -124,15 +108,6 @@ class Notification : public DOMEventTargetHelper, public SupportsWeakPtr {
static NotificationPermission GetPermission(const GlobalObject& aGlobal,
ErrorResult& aRv);
static already_AddRefed<Promise> Get(nsPIDOMWindowInner* aWindow,
const GetNotificationOptions& aFilter,
const nsAString& aScope,
ErrorResult& aRv);
static already_AddRefed<Promise> WorkerGet(
WorkerPrivate* aWorkerPrivate, const GetNotificationOptions& aFilter,
const nsAString& aScope, ErrorResult& aRv);
// Notification implementation of
// ServiceWorkerRegistration.showNotification.
//

View File

@@ -24,7 +24,7 @@ EXPORTS.mozilla.dom.notification += [
"IPCUtils.h",
"NotificationChild.h",
"NotificationParent.h",
"NotificationUtils.h", # Exported for ContentParent for now
"NotificationUtils.h",
]
UNIFIED_SOURCES += [

View File

@@ -5,6 +5,7 @@
include protocol PBackground;
include ClientIPCTypes;
include DOMTypes;
include IPCNavigationPreloadState;
include IPCServiceWorkerRegistrationDescriptor;
@@ -24,6 +25,7 @@ parent:
async Unregister() returns (bool aSuccess, CopyableErrorResult aRv);
async Update(nsCString aNewestWorkerScriptUrl) returns (
IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult aResult);
async GetNotifications(nsString aTag) returns (IPCNotificationsOrError aResult);
// For NavigationPreload interface
async SetNavigationPreloadEnabled(bool aEnabled) returns (bool aSuccess);

View File

@@ -415,28 +415,85 @@ already_AddRefed<Promise> ServiceWorkerRegistration::ShowNotification(
return p.forget();
}
// https://notifications.spec.whatwg.org/#dom-serviceworkerregistration-getnotifications
already_AddRefed<Promise> ServiceWorkerRegistration::GetNotifications(
const GetNotificationOptions& aOptions, ErrorResult& aRv) {
nsIGlobalObject* global = GetParentObject();
// Step 1: Let global be thiss relevant global object.
// Step 2: Let realm be thiss relevant Realm.
nsCOMPtr<nsIGlobalObject> global = GetParentObject();
if (!global) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
NS_ConvertUTF8toUTF16 scope(mDescriptor.Scope());
// Step 3: Let origin be thiss relevant settings objects origin.
// (Done in ServiceWorkerRegistrationProxy::GetNotifications)
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
return Notification::Get(window, aOptions, scope, aRv);
// Step 4: Let promise be a new promise in realm.
RefPtr<Promise> promise = Promise::CreateInfallible(global);
// Step 5: Run these steps in parallel:
// Step 5.1: Let tag be filter["tag"].
// Step 5.2: Let notifications be a list of all notifications in the list of
// notifications whose origin is same origin with origin, whose service worker
// registration is this, and whose tag, if tag is not the empty string, is
// tag.
if (!mActor) {
// While it's not clear from the current spec, it's fair to say that
// unregistered registrations cannot have a match in the step 5.2. See also
// bug 1881812.
// One could also say we should throw here, but no browsers throw.
promise->MaybeResolve(nsTArray<RefPtr<Notification>>());
return promise.forget();
}
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
worker->AssertIsOnWorkerThread();
return Notification::WorkerGet(worker, aOptions, scope, aRv);
// Step 5.3: Queue a global task on the DOM manipulation task source
// given global to run these steps:
mActor->SendGetNotifications(aOptions.mTag)
->Then(GetCurrentSerialEventTarget(), __func__,
[promise, scope = NS_ConvertUTF8toUTF16(mDescriptor.Scope())](
const PServiceWorkerRegistrationChild::
GetNotificationsPromise::ResolveOrRejectValue&& aValue) {
if (aValue.IsReject()) {
// An unregistered registration
promise->MaybeResolve(nsTArray<RefPtr<Notification>>());
return;
}
if (aValue.ResolveValue().type() ==
IPCNotificationsOrError::Tnsresult) {
// An active registration but had some internal error
promise->MaybeRejectWithInvalidStateError(
"Could not retrieve notifications"_ns);
return;
}
const nsTArray<IPCNotification>& notifications =
aValue.ResolveValue().get_ArrayOfIPCNotification();
// Step 5.3.1: Let objects be a list.
nsTArray<RefPtr<Notification>> objects(notifications.Length());
// Step 5.3.2: For each notification in notifications, in
// creation order, create a new Notification object with realm
// representing notification, and append it to objects.
for (const IPCNotification& ipcNotification : notifications) {
auto result = Notification::ConstructFromIPC(
promise->GetParentObject(), ipcNotification, scope);
if (result.isErr()) {
continue;
}
RefPtr<Notification> n = result.unwrap();
objects.AppendElement(n.forget());
}
// Step 5.3.3: Resolve promise with objects.
promise->MaybeResolve(std::move(objects));
});
// Step 6: Return promise.
return promise.forget();
}
void ServiceWorkerRegistration::SetNavigationPreloadEnabled(

View File

@@ -126,6 +126,23 @@ IPCResult ServiceWorkerRegistrationParent::RecvGetNavigationPreloadState(
return IPC_OK();
}
IPCResult ServiceWorkerRegistrationParent::RecvGetNotifications(
const nsAString& aTag, GetNotificationsResolver&& aResolver) {
if (!mProxy) {
aResolver(NS_ERROR_DOM_INVALID_STATE_ERR);
return IPC_OK();
}
mProxy->GetNotifications(aTag)->Then(
GetCurrentSerialEventTarget(), __func__,
[aResolver](const CopyableTArray<IPCNotification>& aNotifications) {
aResolver(aNotifications);
},
[aResolver](nsresult) { aResolver(NS_ERROR_DOM_INVALID_STATE_ERR); });
return IPC_OK();
}
ServiceWorkerRegistrationParent::ServiceWorkerRegistrationParent()
: mDeleteSent(false) {}

View File

@@ -43,6 +43,9 @@ class ServiceWorkerRegistrationParent final
mozilla::ipc::IPCResult RecvGetNavigationPreloadState(
GetNavigationPreloadStateResolver&& aResolver) override;
mozilla::ipc::IPCResult RecvGetNotifications(
const nsAString& aTag, GetNotificationsResolver&& aResolver) override;
public:
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationParent, override);

View File

@@ -8,14 +8,17 @@
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/notification/NotificationUtils.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "ServiceWorkerManager.h"
#include "ServiceWorkerRegistrationParent.h"
#include "ServiceWorkerUnregisterCallback.h"
#include "nsINotificationStorage.h"
namespace mozilla::dom {
using mozilla::ipc::AssertIsOnBackgroundThread;
using namespace mozilla::dom::notification;
class ServiceWorkerRegistrationProxy::DelayedUpdate final
: public nsITimerCallback,
@@ -484,4 +487,86 @@ ServiceWorkerRegistrationProxy::GetNavigationPreloadState() {
return promise;
}
// TODO(krosylight): Ideally this callback shouldn't be needed, see bug 1950116.
class NotificationsCallback : public nsINotificationStorageCallback {
public:
NS_DECL_ISUPPORTS
already_AddRefed<NotificationsPromise> Promise() {
return mPromiseHolder.Ensure(__func__);
}
NS_IMETHOD Handle(const nsAString& aID, const nsAString& aTitle,
const nsAString& aDir, const nsAString& aLang,
const nsAString& aBody, const nsAString& aTag,
const nsAString& aIcon, const nsAString& aData,
const nsAString& aServiceWorkerRegistrationScope) final {
AssertIsOnMainThread();
MOZ_ASSERT(!aID.IsEmpty());
NotificationDirection dir =
StringToEnum<NotificationDirection>(aDir).valueOr(
NotificationDirection::Auto);
// XXX(krosylight): we don't store all notification options
IPCNotification notification(
nsString(aID),
IPCNotificationOptions(nsString(aTitle), dir, nsString(aLang),
nsString(aBody), nsString(aTag), nsString(aIcon),
false, false, nsTArray<uint32_t>(),
nsString(aData)));
mNotifications.AppendElement(std::move(notification));
return NS_OK;
}
NS_IMETHOD Done() final {
mPromiseHolder.Resolve(std::move(mNotifications), __func__);
return NS_OK;
}
protected:
virtual ~NotificationsCallback() = default;
nsTArray<IPCNotification> mNotifications;
MozPromiseHolder<NotificationsPromise> mPromiseHolder;
};
NS_IMPL_ISUPPORTS(NotificationsCallback, nsINotificationStorageCallback)
RefPtr<NotificationsPromise> ServiceWorkerRegistrationProxy::GetNotifications(
const nsAString& aTag) {
AssertIsOnBackgroundThread();
RefPtr<ServiceWorkerRegistrationProxy> self = this;
return InvokeAsync(
GetMainThreadSerialEventTarget(), __func__,
[self, tag = nsString(aTag)]() {
Result<nsCOMPtr<nsIPrincipal>, nsresult> principalResult =
self->mListeningClientInfo.GetPrincipal();
if (principalResult.isErr()) {
return NotificationsPromise::CreateAndReject(
NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
}
nsCOMPtr<nsIPrincipal> principal = principalResult.unwrap();
nsString origin;
GetOrigin(principal, origin);
RefPtr<NotificationsCallback> callback = new NotificationsCallback();
RefPtr<NotificationsPromise> promise = callback->Promise();
nsCOMPtr<nsINotificationStorage> notificationStorage =
GetNotificationStorage(
self->mListeningClientInfo.IsPrivateBrowsing());
nsString scope;
self->GetScope(scope);
notificationStorage->Get(origin, scope, tag, callback);
return promise;
});
}
} // namespace mozilla::dom

View File

@@ -86,6 +86,8 @@ class ServiceWorkerRegistrationProxy final
RefPtr<NavigationPreloadStatePromise> GetNavigationPreloadState();
RefPtr<NotificationsPromise> GetNotifications(const nsAString& aTag);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerRegistrationProxy,
override);
};

View File

@@ -7,6 +7,7 @@
#define _mozilla_dom_ServiceWorkerUtils_h
#include "mozilla/MozPromise.h"
#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/IPCNavigationPreloadState.h"
#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
#include "nsTArray.h"
@@ -36,6 +37,9 @@ using ServiceWorkerRegistrationListPromise =
using NavigationPreloadStatePromise =
MozPromise<IPCNavigationPreloadState, CopyableErrorResult, false>;
using NotificationsPromise =
MozPromise<CopyableTArray<IPCNotification>, nsresult, false>;
using ServiceWorkerRegistrationCallback =
std::function<void(const ServiceWorkerRegistrationDescriptor&)>;

View File

@@ -17,8 +17,8 @@ promise_test(async (t) => {
await registration.showNotification("foo");
await registration.unregister();
const newRegistration = await prepareActiveServiceWorker("noop-sw.js");
const notifications = await newRegistration.getNotifications();
registration = await prepareActiveServiceWorker("noop-sw.js");
const notifications = await registration.getNotifications();
// The spec says notifications should be associated with service worker registration
// and thus unregistering should dissociate the notification.
@@ -29,3 +29,10 @@ promise_test(async (t) => {
// > is not the empty string, is tag.
assert_equals(notifications.length, 0, "Should return zero notification");
}, "A new SW registration gets no previous notification");
promise_test(async (t) => {
await registration.showNotification("foo");
await registration.unregister();
const notifications = await registration.getNotifications();
assert_equals(notifications.length, 0, "Should return zero notification");
}, "An unregistered SW registration gets no previous notification");