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;
|
IPCNotificationOptions options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union IPCNotificationsOrError {
|
||||||
|
IPCNotification[];
|
||||||
|
nsresult;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -70,122 +70,6 @@ struct NotificationStrings {
|
|||||||
const nsString mServiceWorkerRegistrationScope;
|
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,
|
class NotificationPermissionRequest : public ContentPermissionRequestBase,
|
||||||
public nsIRunnable,
|
public nsIRunnable,
|
||||||
public nsINamed {
|
public nsINamed {
|
||||||
@@ -473,36 +357,6 @@ already_AddRefed<Notification> Notification::Constructor(
|
|||||||
return notification.forget();
|
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
|
// static
|
||||||
Result<already_AddRefed<Notification>, QMResult> Notification::ConstructFromIPC(
|
Result<already_AddRefed<Notification>, QMResult> Notification::ConstructFromIPC(
|
||||||
nsIGlobalObject* aGlobal, const IPCNotification& aIPCNotification,
|
nsIGlobalObject* aGlobal, const IPCNotification& aIPCNotification,
|
||||||
@@ -797,200 +651,6 @@ nsresult Notification::ResolveIconURL(nsIGlobalObject* aGlobal,
|
|||||||
return rv;
|
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,
|
JSObject* Notification::WrapObject(JSContext* aCx,
|
||||||
JS::Handle<JSObject*> aGivenProto) {
|
JS::Handle<JSObject*> aGivenProto) {
|
||||||
return mozilla::dom::Notification_Binding::Wrap(aCx, this, 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 GlobalObject& aGlobal, const nsAString& aTitle,
|
||||||
const NotificationOptions& aOption, ErrorResult& aRv);
|
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.
|
* 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,
|
static NotificationPermission GetPermission(const GlobalObject& aGlobal,
|
||||||
ErrorResult& aRv);
|
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
|
// Notification implementation of
|
||||||
// ServiceWorkerRegistration.showNotification.
|
// ServiceWorkerRegistration.showNotification.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ EXPORTS.mozilla.dom.notification += [
|
|||||||
"IPCUtils.h",
|
"IPCUtils.h",
|
||||||
"NotificationChild.h",
|
"NotificationChild.h",
|
||||||
"NotificationParent.h",
|
"NotificationParent.h",
|
||||||
"NotificationUtils.h", # Exported for ContentParent for now
|
"NotificationUtils.h",
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
include protocol PBackground;
|
include protocol PBackground;
|
||||||
|
|
||||||
include ClientIPCTypes;
|
include ClientIPCTypes;
|
||||||
|
include DOMTypes;
|
||||||
include IPCNavigationPreloadState;
|
include IPCNavigationPreloadState;
|
||||||
include IPCServiceWorkerRegistrationDescriptor;
|
include IPCServiceWorkerRegistrationDescriptor;
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ parent:
|
|||||||
async Unregister() returns (bool aSuccess, CopyableErrorResult aRv);
|
async Unregister() returns (bool aSuccess, CopyableErrorResult aRv);
|
||||||
async Update(nsCString aNewestWorkerScriptUrl) returns (
|
async Update(nsCString aNewestWorkerScriptUrl) returns (
|
||||||
IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult aResult);
|
IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult aResult);
|
||||||
|
async GetNotifications(nsString aTag) returns (IPCNotificationsOrError aResult);
|
||||||
|
|
||||||
// For NavigationPreload interface
|
// For NavigationPreload interface
|
||||||
async SetNavigationPreloadEnabled(bool aEnabled) returns (bool aSuccess);
|
async SetNavigationPreloadEnabled(bool aEnabled) returns (bool aSuccess);
|
||||||
|
|||||||
@@ -415,28 +415,85 @@ already_AddRefed<Promise> ServiceWorkerRegistration::ShowNotification(
|
|||||||
return p.forget();
|
return p.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://notifications.spec.whatwg.org/#dom-serviceworkerregistration-getnotifications
|
||||||
already_AddRefed<Promise> ServiceWorkerRegistration::GetNotifications(
|
already_AddRefed<Promise> ServiceWorkerRegistration::GetNotifications(
|
||||||
const GetNotificationOptions& aOptions, ErrorResult& aRv) {
|
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) {
|
if (!global) {
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return nullptr;
|
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()) {
|
// Step 4: Let promise be a new promise in realm.
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
|
RefPtr<Promise> promise = Promise::CreateInfallible(global);
|
||||||
if (NS_WARN_IF(!window)) {
|
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
// Step 5: Run these steps in parallel:
|
||||||
return nullptr;
|
// Step 5.1: Let tag be filter["tag"].
|
||||||
}
|
// Step 5.2: Let notifications be a list of all notifications in the list of
|
||||||
return Notification::Get(window, aOptions, scope, aRv);
|
// 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();
|
// Step 5.3: Queue a global task on the DOM manipulation task source
|
||||||
worker->AssertIsOnWorkerThread();
|
// given global to run these steps:
|
||||||
return Notification::WorkerGet(worker, aOptions, scope, aRv);
|
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(
|
void ServiceWorkerRegistration::SetNavigationPreloadEnabled(
|
||||||
|
|||||||
@@ -126,6 +126,23 @@ IPCResult ServiceWorkerRegistrationParent::RecvGetNavigationPreloadState(
|
|||||||
return IPC_OK();
|
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()
|
ServiceWorkerRegistrationParent::ServiceWorkerRegistrationParent()
|
||||||
: mDeleteSent(false) {}
|
: mDeleteSent(false) {}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class ServiceWorkerRegistrationParent final
|
|||||||
mozilla::ipc::IPCResult RecvGetNavigationPreloadState(
|
mozilla::ipc::IPCResult RecvGetNavigationPreloadState(
|
||||||
GetNavigationPreloadStateResolver&& aResolver) override;
|
GetNavigationPreloadStateResolver&& aResolver) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvGetNotifications(
|
||||||
|
const nsAString& aTag, GetNotificationsResolver&& aResolver) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationParent, override);
|
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistrationParent, override);
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,17 @@
|
|||||||
|
|
||||||
#include "mozilla/SchedulerGroup.h"
|
#include "mozilla/SchedulerGroup.h"
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
|
#include "mozilla/dom/notification/NotificationUtils.h"
|
||||||
#include "mozilla/ipc/BackgroundParent.h"
|
#include "mozilla/ipc/BackgroundParent.h"
|
||||||
#include "ServiceWorkerManager.h"
|
#include "ServiceWorkerManager.h"
|
||||||
#include "ServiceWorkerRegistrationParent.h"
|
#include "ServiceWorkerRegistrationParent.h"
|
||||||
#include "ServiceWorkerUnregisterCallback.h"
|
#include "ServiceWorkerUnregisterCallback.h"
|
||||||
|
#include "nsINotificationStorage.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
using mozilla::ipc::AssertIsOnBackgroundThread;
|
using mozilla::ipc::AssertIsOnBackgroundThread;
|
||||||
|
using namespace mozilla::dom::notification;
|
||||||
|
|
||||||
class ServiceWorkerRegistrationProxy::DelayedUpdate final
|
class ServiceWorkerRegistrationProxy::DelayedUpdate final
|
||||||
: public nsITimerCallback,
|
: public nsITimerCallback,
|
||||||
@@ -484,4 +487,86 @@ ServiceWorkerRegistrationProxy::GetNavigationPreloadState() {
|
|||||||
return promise;
|
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
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ class ServiceWorkerRegistrationProxy final
|
|||||||
|
|
||||||
RefPtr<NavigationPreloadStatePromise> GetNavigationPreloadState();
|
RefPtr<NavigationPreloadStatePromise> GetNavigationPreloadState();
|
||||||
|
|
||||||
|
RefPtr<NotificationsPromise> GetNotifications(const nsAString& aTag);
|
||||||
|
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerRegistrationProxy,
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerRegistrationProxy,
|
||||||
override);
|
override);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define _mozilla_dom_ServiceWorkerUtils_h
|
#define _mozilla_dom_ServiceWorkerUtils_h
|
||||||
|
|
||||||
#include "mozilla/MozPromise.h"
|
#include "mozilla/MozPromise.h"
|
||||||
|
#include "mozilla/dom/DOMTypes.h"
|
||||||
#include "mozilla/dom/IPCNavigationPreloadState.h"
|
#include "mozilla/dom/IPCNavigationPreloadState.h"
|
||||||
#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
|
#include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
@@ -36,6 +37,9 @@ using ServiceWorkerRegistrationListPromise =
|
|||||||
using NavigationPreloadStatePromise =
|
using NavigationPreloadStatePromise =
|
||||||
MozPromise<IPCNavigationPreloadState, CopyableErrorResult, false>;
|
MozPromise<IPCNavigationPreloadState, CopyableErrorResult, false>;
|
||||||
|
|
||||||
|
using NotificationsPromise =
|
||||||
|
MozPromise<CopyableTArray<IPCNotification>, nsresult, false>;
|
||||||
|
|
||||||
using ServiceWorkerRegistrationCallback =
|
using ServiceWorkerRegistrationCallback =
|
||||||
std::function<void(const ServiceWorkerRegistrationDescriptor&)>;
|
std::function<void(const ServiceWorkerRegistrationDescriptor&)>;
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ promise_test(async (t) => {
|
|||||||
|
|
||||||
await registration.showNotification("foo");
|
await registration.showNotification("foo");
|
||||||
await registration.unregister();
|
await registration.unregister();
|
||||||
const newRegistration = await prepareActiveServiceWorker("noop-sw.js");
|
registration = await prepareActiveServiceWorker("noop-sw.js");
|
||||||
const notifications = await newRegistration.getNotifications();
|
const notifications = await registration.getNotifications();
|
||||||
|
|
||||||
// The spec says notifications should be associated with service worker registration
|
// The spec says notifications should be associated with service worker registration
|
||||||
// and thus unregistering should dissociate the notification.
|
// and thus unregistering should dissociate the notification.
|
||||||
@@ -29,3 +29,10 @@ promise_test(async (t) => {
|
|||||||
// > is not the empty string, is tag.
|
// > is not the empty string, is tag.
|
||||||
assert_equals(notifications.length, 0, "Should return zero notification");
|
assert_equals(notifications.length, 0, "Should return zero notification");
|
||||||
}, "A new SW registration gets no previous 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