Bug 1635524 - Part 2: Pass oldSubscription for pushsubscriptionchange event r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D234713
This commit is contained in:
@@ -12,6 +12,7 @@ interface mozIDOMWindowProxy;
|
|||||||
interface nsIArray;
|
interface nsIArray;
|
||||||
interface nsIInterceptedChannel;
|
interface nsIInterceptedChannel;
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
|
interface nsIPushSubscription;
|
||||||
interface nsIRunnable;
|
interface nsIRunnable;
|
||||||
interface nsIURI;
|
interface nsIURI;
|
||||||
%{C++
|
%{C++
|
||||||
@@ -320,7 +321,8 @@ interface nsIServiceWorkerManager : nsISupports
|
|||||||
in ACString aScope,
|
in ACString aScope,
|
||||||
[optional] in Array<uint8_t> aDataBytes);
|
[optional] in Array<uint8_t> aDataBytes);
|
||||||
void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
|
void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
|
||||||
in ACString scope);
|
in ACString scope,
|
||||||
|
[optional] in nsIPushSubscription aOldSubscription);
|
||||||
|
|
||||||
void addListener(in nsIServiceWorkerManagerListener aListener);
|
void addListener(in nsIServiceWorkerManagerListener aListener);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
%}
|
%}
|
||||||
|
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
|
interface nsIPushSubscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires XPCOM observer notifications and service worker events for
|
* Fires XPCOM observer notifications and service worker events for
|
||||||
@@ -43,7 +44,8 @@ interface nsIPushNotifier : nsISupports
|
|||||||
* `pushsubscriptionchange` event to the service worker registered for the
|
* `pushsubscriptionchange` event to the service worker registered for the
|
||||||
* |scope|.
|
* |scope|.
|
||||||
*/
|
*/
|
||||||
void notifySubscriptionChange(in ACString scope, in nsIPrincipal principal);
|
void notifySubscriptionChange(in ACString scope, in nsIPrincipal principal,
|
||||||
|
[optional] in nsIPushSubscription oldSubscription);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires a `push-subscription-modified` observer notification. Chrome code
|
* Fires a `push-subscription-modified` observer notification. Chrome code
|
||||||
|
|||||||
93
dom/push/ChromePushSubscription.sys.mjs
Normal file
93
dom/push/ChromePushSubscription.sys.mjs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/** `ChromePushSubscription` instances are passed to all subscription callbacks. */
|
||||||
|
export class ChromePushSubscription {
|
||||||
|
#props;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
this.#props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryInterface = ChromeUtils.generateQI(["nsIPushSubscription"]);
|
||||||
|
|
||||||
|
/** The URL for sending messages to this subscription. */
|
||||||
|
get endpoint() {
|
||||||
|
return this.#props.endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The last time a message was sent to this subscription. */
|
||||||
|
get lastPush() {
|
||||||
|
return this.#props.lastPush;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The total number of messages sent to this subscription. */
|
||||||
|
get pushCount() {
|
||||||
|
return this.#props.pushCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The number of remaining background messages that can be sent to this
|
||||||
|
* subscription, or -1 of the subscription is exempt from the quota.
|
||||||
|
*/
|
||||||
|
get quota() {
|
||||||
|
return this.#props.quota;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this subscription was created with the system principal.
|
||||||
|
* System subscriptions are exempt from the background message quota and
|
||||||
|
* permission checks.
|
||||||
|
*/
|
||||||
|
get isSystemSubscription() {
|
||||||
|
return !!this.#props.systemRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The private key used to decrypt incoming push messages, in JWK format */
|
||||||
|
get p256dhPrivateKey() {
|
||||||
|
return this.#props.p256dhPrivateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this subscription is subject to the background message
|
||||||
|
* quota.
|
||||||
|
*/
|
||||||
|
quotaApplies() {
|
||||||
|
return this.quota >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether this subscription exceeded the background message quota,
|
||||||
|
* or the user revoked the notification permission. The caller must request a
|
||||||
|
* new subscription to continue receiving push messages.
|
||||||
|
*/
|
||||||
|
isExpired() {
|
||||||
|
return this.quota === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a key for encrypting messages sent to this subscription. JS
|
||||||
|
* callers receive the key buffer as a return value, while C++ callers
|
||||||
|
* receive the key size and buffer as out parameters.
|
||||||
|
*/
|
||||||
|
getKey(name) {
|
||||||
|
switch (name) {
|
||||||
|
case "p256dh":
|
||||||
|
return this.#getRawKey(this.#props.p256dhKey);
|
||||||
|
|
||||||
|
case "auth":
|
||||||
|
return this.#getRawKey(this.#props.authenticationSecret);
|
||||||
|
|
||||||
|
case "appServer":
|
||||||
|
return this.#getRawKey(this.#props.appServerKey);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
#getRawKey(key) {
|
||||||
|
if (!key) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return new Uint8Array(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||||
|
import { ChromePushSubscription } from "./ChromePushSubscription.sys.mjs";
|
||||||
|
|
||||||
var isParent =
|
var isParent =
|
||||||
Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||||
@@ -488,96 +489,6 @@ Object.assign(PushServiceContent.prototype, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/** `ChromePushSubscription` instances are passed to all subscription callbacks. */
|
|
||||||
class ChromePushSubscription {
|
|
||||||
#props;
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
this.#props = props;
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryInterface = ChromeUtils.generateQI(["nsIPushSubscription"]);
|
|
||||||
|
|
||||||
/** The URL for sending messages to this subscription. */
|
|
||||||
get endpoint() {
|
|
||||||
return this.#props.endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The last time a message was sent to this subscription. */
|
|
||||||
get lastPush() {
|
|
||||||
return this.#props.lastPush;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The total number of messages sent to this subscription. */
|
|
||||||
get pushCount() {
|
|
||||||
return this.#props.pushCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The number of remaining background messages that can be sent to this
|
|
||||||
* subscription, or -1 of the subscription is exempt from the quota.
|
|
||||||
*/
|
|
||||||
get quota() {
|
|
||||||
return this.#props.quota;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this subscription was created with the system principal.
|
|
||||||
* System subscriptions are exempt from the background message quota and
|
|
||||||
* permission checks.
|
|
||||||
*/
|
|
||||||
get isSystemSubscription() {
|
|
||||||
return !!this.#props.systemRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The private key used to decrypt incoming push messages, in JWK format */
|
|
||||||
get p256dhPrivateKey() {
|
|
||||||
return this.#props.p256dhPrivateKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this subscription is subject to the background message
|
|
||||||
* quota.
|
|
||||||
*/
|
|
||||||
quotaApplies() {
|
|
||||||
return this.quota >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this subscription exceeded the background message quota,
|
|
||||||
* or the user revoked the notification permission. The caller must request a
|
|
||||||
* new subscription to continue receiving push messages.
|
|
||||||
*/
|
|
||||||
isExpired() {
|
|
||||||
return this.quota === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a key for encrypting messages sent to this subscription. JS
|
|
||||||
* callers receive the key buffer as a return value, while C++ callers
|
|
||||||
* receive the key size and buffer as out parameters.
|
|
||||||
*/
|
|
||||||
getKey(name) {
|
|
||||||
switch (name) {
|
|
||||||
case "p256dh":
|
|
||||||
return this.#getRawKey(this.#props.p256dhKey);
|
|
||||||
|
|
||||||
case "auth":
|
|
||||||
return this.#getRawKey(this.#props.authenticationSecret);
|
|
||||||
|
|
||||||
case "appServer":
|
|
||||||
return this.#getRawKey(this.#props.appServerKey);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
#getRawKey(key) {
|
|
||||||
if (!key) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return new Uint8Array(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the correct implementation depending on whether we're running in
|
// Export the correct implementation depending on whether we're running in
|
||||||
// the parent or content process.
|
// the parent or content process.
|
||||||
export let Service = isParent ? PushServiceParent : PushServiceContent;
|
export let Service = isParent ? PushServiceParent : PushServiceContent;
|
||||||
|
|||||||
@@ -33,34 +33,6 @@
|
|||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
nsresult GetPermissionState(nsIPrincipal* aPrincipal, PermissionState& aState) {
|
|
||||||
nsCOMPtr<nsIPermissionManager> permManager =
|
|
||||||
mozilla::components::PermissionManager::Service();
|
|
||||||
|
|
||||||
if (!permManager) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
|
|
||||||
nsresult rv = permManager->TestExactPermissionFromPrincipal(
|
|
||||||
aPrincipal, "desktop-notification"_ns, &permission);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permission == nsIPermissionManager::ALLOW_ACTION ||
|
|
||||||
Preferences::GetBool("dom.push.testing.ignorePermission", false)) {
|
|
||||||
aState = PermissionState::Granted;
|
|
||||||
} else if (permission == nsIPermissionManager::DENY_ACTION) {
|
|
||||||
aState = PermissionState::Denied;
|
|
||||||
} else {
|
|
||||||
aState = PermissionState::Prompt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult GetSubscriptionParams(nsIPushSubscription* aSubscription,
|
nsresult GetSubscriptionParams(nsIPushSubscription* aSubscription,
|
||||||
nsAString& aEndpoint,
|
nsAString& aEndpoint,
|
||||||
nsTArray<uint8_t>& aRawP256dhKey,
|
nsTArray<uint8_t>& aRawP256dhKey,
|
||||||
@@ -91,6 +63,34 @@ nsresult GetSubscriptionParams(nsIPushSubscription* aSubscription,
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
nsresult GetPermissionState(nsIPrincipal* aPrincipal, PermissionState& aState) {
|
||||||
|
nsCOMPtr<nsIPermissionManager> permManager =
|
||||||
|
mozilla::components::PermissionManager::Service();
|
||||||
|
|
||||||
|
if (!permManager) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
|
||||||
|
nsresult rv = permManager->TestExactPermissionFromPrincipal(
|
||||||
|
aPrincipal, "desktop-notification"_ns, &permission);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission == nsIPermissionManager::ALLOW_ACTION ||
|
||||||
|
Preferences::GetBool("dom.push.testing.ignorePermission", false)) {
|
||||||
|
aState = PermissionState::Granted;
|
||||||
|
} else if (permission == nsIPermissionManager::DENY_ACTION) {
|
||||||
|
aState = PermissionState::Denied;
|
||||||
|
} else {
|
||||||
|
aState = PermissionState::Prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
class GetSubscriptionResultRunnable final : public WorkerThreadRunnable {
|
class GetSubscriptionResultRunnable final : public WorkerThreadRunnable {
|
||||||
public:
|
public:
|
||||||
GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate,
|
GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
class nsIGlobalObject;
|
class nsIGlobalObject;
|
||||||
class nsIPrincipal;
|
class nsIPrincipal;
|
||||||
|
class nsIPushSubscription;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
class ErrorResult;
|
class ErrorResult;
|
||||||
@@ -49,6 +50,12 @@ class PushManagerImpl;
|
|||||||
struct PushSubscriptionOptionsInit;
|
struct PushSubscriptionOptionsInit;
|
||||||
class WorkerPrivate;
|
class WorkerPrivate;
|
||||||
|
|
||||||
|
nsresult GetSubscriptionParams(nsIPushSubscription* aSubscription,
|
||||||
|
nsAString& aEndpoint,
|
||||||
|
nsTArray<uint8_t>& aRawP256dhKey,
|
||||||
|
nsTArray<uint8_t>& aAuthSecret,
|
||||||
|
nsTArray<uint8_t>& aAppServerKey);
|
||||||
|
|
||||||
class PushManager final : public nsISupports, public nsWrapperCache {
|
class PushManager final : public nsISupports, public nsWrapperCache {
|
||||||
public:
|
public:
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsICategoryManager.h"
|
#include "nsICategoryManager.h"
|
||||||
|
#include "nsIPushService.h"
|
||||||
#include "nsIXULRuntime.h"
|
#include "nsIXULRuntime.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsXPCOM.h"
|
#include "nsXPCOM.h"
|
||||||
@@ -66,9 +67,11 @@ PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
|
|||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
|
PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
|
||||||
nsIPrincipal* aPrincipal) {
|
nsIPrincipal* aPrincipal,
|
||||||
|
nsIPushSubscription* aOldSubscription) {
|
||||||
NS_ENSURE_ARG(aPrincipal);
|
NS_ENSURE_ARG(aPrincipal);
|
||||||
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
|
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal,
|
||||||
|
aOldSubscription);
|
||||||
return Dispatch(dispatcher);
|
return Dispatch(dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,8 +314,9 @@ bool PushMessageDispatcher::SendToChild(ContentParent* aContentActor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(
|
PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(
|
||||||
const nsACString& aScope, nsIPrincipal* aPrincipal)
|
const nsACString& aScope, nsIPrincipal* aPrincipal,
|
||||||
: PushDispatcher(aScope, aPrincipal) {}
|
nsIPushSubscription* aOldSubscription)
|
||||||
|
: PushDispatcher(aScope, aPrincipal), mOldSubscription(aOldSubscription) {}
|
||||||
|
|
||||||
PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher() = default;
|
PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher() = default;
|
||||||
|
|
||||||
@@ -334,7 +338,8 @@ nsresult PushSubscriptionChangeDispatcher::NotifyWorkers() {
|
|||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
|
return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope,
|
||||||
|
mOldSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PushSubscriptionChangeDispatcher::SendToParent(
|
bool PushSubscriptionChangeDispatcher::SendToParent(
|
||||||
|
|||||||
@@ -149,13 +149,17 @@ class PushMessageDispatcher final : public PushDispatcher {
|
|||||||
class PushSubscriptionChangeDispatcher final : public PushDispatcher {
|
class PushSubscriptionChangeDispatcher final : public PushDispatcher {
|
||||||
public:
|
public:
|
||||||
PushSubscriptionChangeDispatcher(const nsACString& aScope,
|
PushSubscriptionChangeDispatcher(const nsACString& aScope,
|
||||||
nsIPrincipal* aPrincipal);
|
nsIPrincipal* aPrincipal,
|
||||||
|
nsIPushSubscription* aOldSubscription);
|
||||||
~PushSubscriptionChangeDispatcher();
|
~PushSubscriptionChangeDispatcher();
|
||||||
|
|
||||||
nsresult NotifyObservers() override;
|
nsresult NotifyObservers() override;
|
||||||
nsresult NotifyWorkers() override;
|
nsresult NotifyWorkers() override;
|
||||||
bool SendToParent(ContentChild* aParentActor) override;
|
bool SendToParent(ContentChild* aParentActor) override;
|
||||||
bool SendToChild(ContentParent* aContentActor) override;
|
bool SendToChild(ContentParent* aContentActor) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsCOMPtr<nsIPushSubscription> mOldSubscription;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PushSubscriptionModifiedDispatcher : public PushDispatcher {
|
class PushSubscriptionModifiedDispatcher : public PushDispatcher {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
||||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||||
|
import { ChromePushSubscription } from "./ChromePushSubscription.sys.mjs";
|
||||||
|
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
@@ -661,7 +662,11 @@ export var PushService = {
|
|||||||
if (!record) {
|
if (!record) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lazy.gPushNotifier.notifySubscriptionChange(record.scope, record.principal);
|
lazy.gPushNotifier.notifySubscriptionChange(
|
||||||
|
record.scope,
|
||||||
|
record.principal,
|
||||||
|
new ChromePushSubscription(record.toSubscription())
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ EXTRA_COMPONENTS += [
|
|||||||
]
|
]
|
||||||
|
|
||||||
EXTRA_JS_MODULES += [
|
EXTRA_JS_MODULES += [
|
||||||
|
"ChromePushSubscription.sys.mjs",
|
||||||
"Push.sys.mjs",
|
"Push.sys.mjs",
|
||||||
"PushBroadcastService.sys.mjs",
|
"PushBroadcastService.sys.mjs",
|
||||||
"PushComponents.sys.mjs",
|
"PushComponents.sys.mjs",
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
const { MockRegistrar } = ChromeUtils.importESModule(
|
const { MockRegistrar } = ChromeUtils.importESModule(
|
||||||
"resource://testing-common/MockRegistrar.sys.mjs"
|
"resource://testing-common/MockRegistrar.sys.mjs"
|
||||||
);
|
);
|
||||||
|
const { ChromePushSubscription } = ChromeUtils.importESModule(
|
||||||
|
"resource://gre/modules/ChromePushSubscription.sys.mjs"
|
||||||
|
);
|
||||||
|
|
||||||
let pushService = Cc["@mozilla.org/push/Service;1"].getService(
|
let pushService = Cc["@mozilla.org/push/Service;1"].getService(
|
||||||
Ci.nsIPushService
|
Ci.nsIPushService
|
||||||
@@ -54,7 +57,21 @@ add_test(function test_service_instantiation() {
|
|||||||
equal(handlerService.observed[0].data, scope);
|
equal(handlerService.observed[0].data, scope);
|
||||||
|
|
||||||
// and a subscription change.
|
// and a subscription change.
|
||||||
pushNotifier.notifySubscriptionChange(scope, principal);
|
pushNotifier.notifySubscriptionChange(
|
||||||
|
scope,
|
||||||
|
principal,
|
||||||
|
new ChromePushSubscription({
|
||||||
|
endpoint: "xpcshell",
|
||||||
|
lastPush: 0,
|
||||||
|
pushCount: 0,
|
||||||
|
p256dhKey: [],
|
||||||
|
p256dhPrivateKey: [],
|
||||||
|
authenticationSecret: [],
|
||||||
|
appServerKey: [],
|
||||||
|
quota: 0,
|
||||||
|
systemRecord: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
equal(handlerService.observed.length, 2);
|
equal(handlerService.observed.length, 2);
|
||||||
equal(handlerService.observed[1].topic, pushService.subscriptionChangeTopic);
|
equal(handlerService.observed[1].topic, pushService.subscriptionChangeTopic);
|
||||||
equal(handlerService.observed[1].subject, principal);
|
equal(handlerService.observed[1].subject, principal);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsDebug.h"
|
#include "nsDebug.h"
|
||||||
#include "nsIPermissionManager.h"
|
#include "nsIPermissionManager.h"
|
||||||
|
#include "nsIPushService.h"
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
@@ -1113,7 +1114,8 @@ nsresult ServiceWorkerManager::SendPushEvent(
|
|||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
ServiceWorkerManager::SendPushSubscriptionChangeEvent(
|
ServiceWorkerManager::SendPushSubscriptionChangeEvent(
|
||||||
const nsACString& aOriginAttributes, const nsACString& aScope) {
|
const nsACString& aOriginAttributes, const nsACString& aScope,
|
||||||
|
nsIPushSubscription* aOldSubscription) {
|
||||||
OriginAttributes attrs;
|
OriginAttributes attrs;
|
||||||
if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
|
if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
|
||||||
return NS_ERROR_INVALID_ARG;
|
return NS_ERROR_INVALID_ARG;
|
||||||
@@ -1123,7 +1125,8 @@ ServiceWorkerManager::SendPushSubscriptionChangeEvent(
|
|||||||
if (!info) {
|
if (!info) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
|
return info->WorkerPrivate()->SendPushSubscriptionChangeEvent(
|
||||||
|
aOldSubscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ServiceWorkerManager::SendNotificationEvent(
|
nsresult ServiceWorkerManager::SendNotificationEvent(
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
#include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack
|
#include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/PushSubscriptionChangeEvent.h"
|
||||||
|
#include "mozilla/dom/PushSubscriptionChangeEventBinding.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsDebug.h"
|
#include "nsDebug.h"
|
||||||
@@ -830,12 +832,28 @@ class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
|
|||||||
|
|
||||||
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
|
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
|
||||||
|
|
||||||
ExtendableEventInit init;
|
ServiceWorkerPushSubscriptionChangeEventOpArgs& args =
|
||||||
|
mArgs.get_ServiceWorkerPushSubscriptionChangeEventOpArgs();
|
||||||
|
|
||||||
|
PushSubscriptionChangeEventInit init;
|
||||||
init.mBubbles = false;
|
init.mBubbles = false;
|
||||||
init.mCancelable = false;
|
init.mCancelable = false;
|
||||||
|
|
||||||
RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
|
if (args.oldSubscription()) {
|
||||||
target, u"pushsubscriptionchange"_ns, init);
|
PushSubscriptionData oldSubscriptionData =
|
||||||
|
args.oldSubscription().extract();
|
||||||
|
RefPtr<PushSubscription> oldSubscription = new PushSubscription(
|
||||||
|
target->GetParentObject(), oldSubscriptionData.endpoint(), u""_ns,
|
||||||
|
Nullable<EpochTimeStamp>(),
|
||||||
|
std::move(oldSubscriptionData.rawP256dhKey()),
|
||||||
|
std::move(oldSubscriptionData.authSecret()),
|
||||||
|
std::move(oldSubscriptionData.appServerKey()));
|
||||||
|
init.mOldSubscription = oldSubscription.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<PushSubscriptionChangeEvent> event =
|
||||||
|
PushSubscriptionChangeEvent::Constructor(
|
||||||
|
target, u"pushsubscriptionchange"_ns, init);
|
||||||
event->SetTrusted(true);
|
event->SetTrusted(true);
|
||||||
|
|
||||||
nsresult rv = DispatchExtendableEventOnWorkerScope(
|
nsresult rv = DispatchExtendableEventOnWorkerScope(
|
||||||
|
|||||||
@@ -42,7 +42,16 @@ struct ServiceWorkerPushEventOpArgs {
|
|||||||
OptionalPushData data;
|
OptionalPushData data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ServiceWorkerPushSubscriptionChangeEventOpArgs {};
|
struct PushSubscriptionData {
|
||||||
|
nsString endpoint;
|
||||||
|
uint8_t[] rawP256dhKey;
|
||||||
|
uint8_t[] authSecret;
|
||||||
|
uint8_t[] appServerKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ServiceWorkerPushSubscriptionChangeEventOpArgs {
|
||||||
|
PushSubscriptionData? oldSubscription;
|
||||||
|
};
|
||||||
|
|
||||||
struct ServiceWorkerNotificationEventOpArgs {
|
struct ServiceWorkerNotificationEventOpArgs {
|
||||||
nsString eventName;
|
nsString eventName;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "mozilla/dom/FetchEventOpChild.h"
|
#include "mozilla/dom/FetchEventOpChild.h"
|
||||||
#include "mozilla/dom/InternalHeaders.h"
|
#include "mozilla/dom/InternalHeaders.h"
|
||||||
#include "mozilla/dom/InternalRequest.h"
|
#include "mozilla/dom/InternalRequest.h"
|
||||||
|
#include "mozilla/dom/PushManager.h"
|
||||||
#include "mozilla/dom/ReferrerInfo.h"
|
#include "mozilla/dom/ReferrerInfo.h"
|
||||||
#include "mozilla/dom/RemoteType.h"
|
#include "mozilla/dom/RemoteType.h"
|
||||||
#include "mozilla/dom/RemoteWorkerControllerChild.h"
|
#include "mozilla/dom/RemoteWorkerControllerChild.h"
|
||||||
@@ -970,12 +971,22 @@ nsresult ServiceWorkerPrivate::SendPushEventInternal(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ServiceWorkerPrivate::SendPushSubscriptionChangeEvent() {
|
nsresult ServiceWorkerPrivate::SendPushSubscriptionChangeEvent(
|
||||||
|
const RefPtr<nsIPushSubscription>& aOldSubscription) {
|
||||||
AssertIsOnMainThread();
|
AssertIsOnMainThread();
|
||||||
|
|
||||||
|
ServiceWorkerPushSubscriptionChangeEventOpArgs args{};
|
||||||
|
if (aOldSubscription) {
|
||||||
|
PushSubscriptionData oldSubscription{};
|
||||||
|
MOZ_TRY(GetSubscriptionParams(aOldSubscription, oldSubscription.endpoint(),
|
||||||
|
oldSubscription.rawP256dhKey(),
|
||||||
|
oldSubscription.authSecret(),
|
||||||
|
oldSubscription.appServerKey()));
|
||||||
|
args.oldSubscription().emplace(oldSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
return ExecServiceWorkerOp(
|
return ExecServiceWorkerOp(
|
||||||
ServiceWorkerPushSubscriptionChangeEventOpArgs(),
|
std::move(args), ServiceWorkerLifetimeExtension(FullLifetimeExtension{}),
|
||||||
ServiceWorkerLifetimeExtension(FullLifetimeExtension{}),
|
|
||||||
[](ServiceWorkerOpResult&& aResult) {
|
[](ServiceWorkerOpResult&& aResult) {
|
||||||
MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
|
MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#define NOTIFICATION_CLOSE_EVENT_NAME u"notificationclose"
|
#define NOTIFICATION_CLOSE_EVENT_NAME u"notificationclose"
|
||||||
|
|
||||||
class nsIInterceptedChannel;
|
class nsIInterceptedChannel;
|
||||||
|
class nsIPushSubscription;
|
||||||
class nsIWorkerDebugger;
|
class nsIWorkerDebugger;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
@@ -107,7 +108,8 @@ class ServiceWorkerPrivate final : public RemoteWorkerObserver {
|
|||||||
const Maybe<nsTArray<uint8_t>>& aData,
|
const Maybe<nsTArray<uint8_t>>& aData,
|
||||||
RefPtr<ServiceWorkerRegistrationInfo> aRegistration);
|
RefPtr<ServiceWorkerRegistrationInfo> aRegistration);
|
||||||
|
|
||||||
nsresult SendPushSubscriptionChangeEvent();
|
nsresult SendPushSubscriptionChangeEvent(
|
||||||
|
const RefPtr<nsIPushSubscription>& aOldSubscription);
|
||||||
|
|
||||||
nsresult SendNotificationEvent(const nsAString& aEventName,
|
nsresult SendNotificationEvent(const nsAString& aEventName,
|
||||||
const nsAString& aID, const nsAString& aTitle,
|
const nsAString& aID, const nsAString& aTitle,
|
||||||
|
|||||||
1
testing/web-platform/mozilla/meta/push-api/__dir__.ini
Normal file
1
testing/web-platform/mozilla/meta/push-api/__dir__.ini
Normal file
@@ -0,0 +1 @@
|
|||||||
|
prefs: [dom.push.serverURL:wss://web-platform.test:8889/mozilla_push_dummy,dom.push.connection.enabled:true]
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[pushsubscriptionchange.https.any.window-module.html]
|
||||||
|
[Fire pushsubscriptionchange event when permission is revoked]
|
||||||
|
expected:
|
||||||
|
if os == "android": FAIL
|
||||||
15
testing/web-platform/mozilla/tests/push-api/push-sw.js
Normal file
15
testing/web-platform/mozilla/tests/push-api/push-sw.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
async function postAll(data) {
|
||||||
|
const clients = await self.clients.matchAll({ includeUncontrolled: true });
|
||||||
|
for (const client of clients) {
|
||||||
|
client.postMessage(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onpushsubscriptionchange = ev => {
|
||||||
|
postAll({
|
||||||
|
type: ev.type,
|
||||||
|
constructor: ev.constructor.name,
|
||||||
|
oldSubscription: ev.oldSubscription?.toJSON(),
|
||||||
|
newSubscription: ev.newSubscription?.toJSON(),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// META: global=window-module
|
||||||
|
// META: script=/resources/testdriver.js
|
||||||
|
// META: script=/resources/testdriver-vendor.js
|
||||||
|
// META: script=/notifications/resources/helpers.js
|
||||||
|
|
||||||
|
let registration;
|
||||||
|
|
||||||
|
promise_setup(async () => {
|
||||||
|
await trySettingPermission("granted");
|
||||||
|
registration = await getActiveServiceWorker("push-sw.js");
|
||||||
|
});
|
||||||
|
|
||||||
|
promise_test(async (t) => {
|
||||||
|
const promise = new Promise(r => {
|
||||||
|
navigator.serviceWorker.addEventListener("message", r, { once: true })
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscription = await registration.pushManager.subscribe();
|
||||||
|
t.add_cleanup(() => subscription.unsubscribe());
|
||||||
|
|
||||||
|
// https://w3c.github.io/push-api/#security-and-privacy-considerations
|
||||||
|
// When a permission is revoked, the user agent MAY fire the "pushsubscriptionchange"
|
||||||
|
// event for subscriptions created with that permission
|
||||||
|
//
|
||||||
|
// But Firefox fires pushsubscriptionchange on permission regrant instead of revocation.
|
||||||
|
// https://github.com/w3c/push-api/issues/236
|
||||||
|
await trySettingPermission("prompt");
|
||||||
|
await trySettingPermission("granted");
|
||||||
|
|
||||||
|
const pushSubscriptionChangeEvent = await promise;
|
||||||
|
|
||||||
|
assert_equals(pushSubscriptionChangeEvent.data.type, "pushsubscriptionchange");
|
||||||
|
assert_equals(pushSubscriptionChangeEvent.data.constructor, "PushSubscriptionChangeEvent");
|
||||||
|
assert_object_equals(pushSubscriptionChangeEvent.data.oldSubscription, subscription.toJSON());
|
||||||
|
}, "Fire pushsubscriptionchange event when permission is revoked");
|
||||||
Reference in New Issue
Block a user