Bug 1950117 - Add PServiceWorkerRegistration::GetNotifications r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D239793
This commit is contained in:
@@ -359,5 +359,10 @@ struct IPCNotification {
|
||||
IPCNotificationOptions options;
|
||||
};
|
||||
|
||||
union IPCNotificationsOrError {
|
||||
IPCNotification[];
|
||||
nsresult;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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 += [
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 this’s relevant global object.
|
||||
// Step 2: Let realm be this’s 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 this’s relevant settings object’s 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(
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,6 +86,8 @@ class ServiceWorkerRegistrationProxy final
|
||||
|
||||
RefPtr<NavigationPreloadStatePromise> GetNavigationPreloadState();
|
||||
|
||||
RefPtr<NotificationsPromise> GetNotifications(const nsAString& aTag);
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerRegistrationProxy,
|
||||
override);
|
||||
};
|
||||
|
||||
@@ -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&)>;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user