Bug 1189998, Part 1 - Consolidate Push client interfaces. r=mt,dragana

--HG--
extra : commitid : GZtcmH86Wg4
extra : rebase_source : b1f7410370991819a29c9142d6dee11261f09905
This commit is contained in:
Kit Cambridge 2015-12-08 15:41:41 -05:00
parent 4cd6379278
commit d1e4feb4e5
17 changed files with 729 additions and 638 deletions

View File

@ -647,9 +647,11 @@
@RESPATH@/components/AppsService.manifest
@RESPATH@/components/Push.js
@RESPATH@/components/Push.manifest
@RESPATH@/components/PushClient.js
@RESPATH@/components/PushNotificationService.js
#ifdef MOZ_SIMPLEPUSH
@RESPATH@/components/PushComponents.js
#else
@RESPATH@/components/PushServiceLauncher.js
#endif
@RESPATH@/components/InterAppComm.manifest
@RESPATH@/components/InterAppCommService.js

View File

@ -547,8 +547,7 @@
@RESPATH@/components/AlarmsManager.manifest
@RESPATH@/components/Push.js
@RESPATH@/components/Push.manifest
@RESPATH@/components/PushClient.js
@RESPATH@/components/PushNotificationService.js
@RESPATH@/components/PushComponents.js
@RESPATH@/components/SlowScriptDebug.manifest
@RESPATH@/components/SlowScriptDebug.js

View File

@ -5,9 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIPushClient.idl',
'nsIPushNotificationService.idl',
'nsIPushObserverNotification.idl',
'nsIPushService.idl',
]
XPIDL_MODULE = 'dom_push'

View File

@ -1,58 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIPrincipal;
/**
* Satisfies contracts similar to the Push API specification.
*
* If status is not NS_OK, endpoint should be ignored. When subscribing to
* a new endpoint, endpoint will be a valid URL on success, when querying for
* the presence of an existing subscription, this will be an empty string if
* the calling {scope+principal} does not currently have an associated
* endpoint.
*/
[scriptable, uuid(d83e398f-9920-4451-b23a-6d5a5ad2fa26)]
interface nsIPushEndpointCallback : nsISupports
{
void onPushEndpoint(in nsresult status,
in DOMString endpoint,
in uint32_t keyLen,
[array, size_is(keyLen)] in octet key,
in uint32_t authSecretLen,
[array, size_is(authSecretLen)] in octet authSecret);
};
/**
* Satisfies contracts similar to the Push API specification.
*
* If status is not NS_OK, there was a problem unsubscribing and success should
* be ignored. success is true if unsubscribing was successful and false if
* there was no subscription.
*/
[scriptable, uuid(9522934d-e844-4f2f-81e8-48c3947b44de)]
interface nsIUnsubscribeResultCallback : nsISupports
{
void onUnsubscribe(in nsresult status, in bool success);
};
/**
* Provides an XPIDL component to interact with the PushService from content
* processes. Unlike PushManager, this has no relationship to the DOM and is
* not exposed to web content. This was added to allow ServiceWorkers to use
* it by dispatching appropriate runnables to the main thread.
*/
[scriptable, uuid(6622d599-439e-4ad1-af32-c941bd2b9968)]
interface nsIPushClient : nsISupports
{
void subscribe(in DOMString scope, in nsIPrincipal principal, in nsIPushEndpointCallback callback);
void unsubscribe(in DOMString scope, in nsIPrincipal principal, in nsIUnsubscribeResultCallback callback);
void getSubscription(in DOMString scope, in nsIPrincipal principal, in nsIPushEndpointCallback callback);
};

View File

@ -1,77 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
/**
* A service for components to subscribe and receive push messages from web
* services. This functionality is exposed to content via the Push API, which
* uses service workers to notify applications. This interface exists to allow
* privileged code to receive messages without migrating to service workers.
*/
[scriptable, uuid(74586476-d73f-4867-bece-87c1dea35750)]
interface nsIPushNotificationService : nsISupports
{
/**
* Creates a push subscription for the given |scope| URL and |pageURL|.
* Returns a promise for the new subscription record, or the existing
* record if this |scope| already has a subscription.
*
* The |pushEndpoint| property of the subscription record is a URL string
* that can be used to send push messages to subscribers. For details,
* please see the Simple Push protocol docs.
*
* Each incoming message fires a `push-notification` observer
* notification, with an `nsIPushObserverNotification` as the subject and
* the |scope| as the data.
*
* If the server drops a subscription, a `push-subscription-change` observer
* will be fired, with the subject set to `null` and the data set to |scope|.
* Servers may drop subscriptions at any time, so callers should recreate
* subscriptions if desired.
*/
jsval register(in string scope, in jsval originAttributes);
/**
* Revokes a push subscription for the given |scope|. Returns a promise
* for the revoked subscription record, or `null` if the |scope| is not
* subscribed to receive notifications.
*/
jsval unregister(in string scope, in jsval originAttributes);
/**
* Returns a promise for the subscription record associated with the
* given |scope|, or `null` if the |scope| does not have a subscription.
*/
jsval registration(in string scope, in jsval originAttributes);
/**
* Clear all subscriptions.
*/
jsval clearAll();
/**
* Clear subscriptions for a domain.
*/
jsval clearForDomain(in string domain);
};
[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)]
interface nsIPushQuotaManager : nsISupports
{
/**
* Informs the quota manager that a notification
* for the given origin has been shown. Used to
* determine if push quota should be relaxed.
*/
void notificationForOriginShown(in string origin);
/**
* Informs the quota manager that a notification
* for the given origin has been closed. Used to
* determine if push quota should be relaxed.
*/
void notificationForOriginClosed(in string origin);
};

View File

@ -6,8 +6,8 @@
#include "nsISupports.idl"
/**
* A push message received by an `nsIPushNotificationService`, used as the
* subject of a `push-notification` observer notification.
* A push message received by an `nsIPushService`, used as the subject of a
* `push-notification` observer notification.
*/
[scriptable, uuid(56f57607-28b6-44b0-aa56-3d4d3c88be15)]
interface nsIPushObserverNotification : nsISupports

View File

@ -0,0 +1,117 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"
interface nsIPrincipal;
/**
* A push subscription, passed as an argument to a subscription callback.
* Similar to the `PushSubscription` WebIDL interface.
*/
[scriptable, uuid(1de32d5c-ea88-4c9e-9626-b032bd87f415)]
interface nsIPushSubscription : nsISupports
{
readonly attribute DOMString endpoint;
readonly attribute long long pushCount;
readonly attribute long long lastPush;
readonly attribute long quota;
bool quotaApplies();
bool isExpired();
void getKey(in DOMString name,
[optional] out uint32_t keyLen,
[array, size_is(keyLen), retval] out uint8_t key);
};
/**
* Called by methods that return a push subscription. A non-success
* |status| indicates that there was a problem returning the
* subscription, and the |subscription| argument should be ignored. Otherwise,
* |subscription| will point to a valid push subscription, or |null| if the
* subscription does not exist.
*/
[scriptable, uuid(1799c074-9d52-46b0-ab3c-c09790732f6f), function]
interface nsIPushSubscriptionCallback : nsISupports
{
void onPushSubscription(in nsresult status,
in nsIPushSubscription subscription);
};
/**
* Called by the |unsubscribe| method. A non-success |status| indicates that
* there was a problem unsubscribing, and the |success| argument should be
* ignored. Otherwise, |success| is true if unsubscribing was successful, and
* false if the subscription does not exist.
*/
[scriptable, uuid(d574118f-61a9-4270-b1f6-4461aa85c4f5), function]
interface nsIUnsubscribeResultCallback : nsISupports
{
void onUnsubscribe(in nsresult status, in bool success);
};
/**
* A service for components to subscribe and receive push messages from web
* services. This functionality is exposed to content via the Push DOM API,
* which uses service workers. This interface exists to support the DOM API,
* and allows privileged code to receive messages without migrating to service
* workers.
*/
[scriptable, uuid(cf46905d-fb55-4f21-8927-31f821c371b6)]
interface nsIPushService : nsISupports
{
/**
* Creates a push subscription for the given |scope| URL and |principal|.
* If a subscription already exists for this |(scope, principal)| pair,
* the callback will receive the existing record as the second argument.
*
* The |endpoint| property of the subscription record is a URL string
* that can be used to send push messages to subscribers.
*
* Each incoming message fires a `push-notification` observer
* notification, with an `nsIPushObserverNotification` as the subject and
* the |scope| as the data.
*
* If the server drops a subscription, a `push-subscription-change` observer
* will be fired, with the subject set to `null` and the data set to |scope|.
* Servers may drop subscriptions at any time, so callers should recreate
* subscriptions if desired.
*/
void subscribe(in DOMString scope, in nsIPrincipal principal,
in nsIPushSubscriptionCallback callback);
/**
* Removes a push subscription for the given |scope|.
*/
void unsubscribe(in DOMString scope, in nsIPrincipal principal,
in nsIUnsubscribeResultCallback callback);
/**
* Retrieves the subscription record associated with the given
* |(scope, principal)| pair. If the subscription does not exist, the
* callback will receive |null| as the second argument.
*/
void getSubscription(in DOMString scope, in nsIPrincipal principal,
in nsIPushSubscriptionCallback callback);
};
[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)]
interface nsIPushQuotaManager : nsISupports
{
/**
* Informs the quota manager that a notification
* for the given origin has been shown. Used to
* determine if push quota should be relaxed.
*/
void notificationForOriginShown(in string origin);
/**
* Informs the quota manager that a notification
* for the given origin has been closed. Used to
* determine if push quota should be relaxed.
*/
void notificationForOriginClosed(in string origin);
};

View File

@ -52,7 +52,7 @@
#endif
#ifndef MOZ_SIMPLEPUSH
#include "nsIPushNotificationService.h"
#include "nsIPushService.h"
#endif
namespace mozilla {
@ -1203,7 +1203,7 @@ NotificationObserver::AdjustPushQuota(const char* aTopic)
return NS_ERROR_NOT_IMPLEMENTED;
#else
nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
do_GetService("@mozilla.org/push/NotificationService;1");
do_GetService("@mozilla.org/push/Service;1");
if (!pushQuotaManager) {
return NS_ERROR_FAILURE;
}

View File

@ -21,6 +21,9 @@ XPCOMUtils.defineLazyGetter(this, "console", () => {
});
});
XPCOMUtils.defineLazyServiceGetter(this, "PushService",
"@mozilla.org/push/Service;1", "nsIPushService");
const PUSH_CID = Components.ID("{cde1d019-fad8-4044-b141-65fb4fb7a245}");
/**
@ -51,8 +54,6 @@ Push.prototype = {
this.initDOMRequestHelper(aWindow);
this._principal = aWindow.document.nodePrincipal;
this._client = Cc["@mozilla.org/push/PushClient;1"].createInstance(Ci.nsIPushClient);
},
setScope: function(scope){
@ -96,8 +97,8 @@ Push.prototype = {
histogram.add(true);
return this.askPermission().then(() =>
this.createPromise((resolve, reject) => {
let callback = new PushEndpointCallback(this, resolve, reject);
this._client.subscribe(this._scope, this._principal, callback);
let callback = new PushSubscriptionCallback(this, resolve, reject);
PushService.subscribe(this._scope, this._principal, callback);
})
);
},
@ -106,8 +107,8 @@ Push.prototype = {
console.debug("getSubscription()", this._scope);
return this.createPromise((resolve, reject) => {
let callback = new PushEndpointCallback(this, resolve, reject);
this._client.getSubscription(this._scope, this._principal, callback);
let callback = new PushSubscriptionCallback(this, resolve, reject);
PushService.getSubscription(this._scope, this._principal, callback);
});
},
@ -178,16 +179,16 @@ Push.prototype = {
},
};
function PushEndpointCallback(pushManager, resolve, reject) {
function PushSubscriptionCallback(pushManager, resolve, reject) {
this.pushManager = pushManager;
this.resolve = resolve;
this.reject = reject;
}
PushEndpointCallback.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
onPushEndpoint: function(ok, endpoint, keyLen, key,
authSecretLen, authSecretIn) {
PushSubscriptionCallback.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushSubscriptionCallback]),
onPushSubscription: function(ok, subscription) {
let {pushManager} = this;
if (!Components.isSuccessCode(ok)) {
this.reject(new pushManager._window.DOMException(
@ -197,32 +198,32 @@ PushEndpointCallback.prototype = {
return;
}
if (!endpoint) {
if (!subscription) {
this.resolve(null);
return;
}
let publicKey = null;
if (keyLen) {
publicKey = new ArrayBuffer(keyLen);
let keyView = new Uint8Array(publicKey);
keyView.set(key);
}
let authSecret = null;
if (authSecretLen) {
authSecret = new ArrayBuffer(authSecretLen);
let secretView = new Uint8Array(authSecret);
secretView.set(authSecretIn);
}
let sub = new pushManager._window.PushSubscription(endpoint,
let publicKey = this._getKey(subscription, "p256dh");
let authSecret = this._getKey(subscription, "auth");
let sub = new pushManager._window.PushSubscription(subscription.endpoint,
pushManager._scope,
publicKey,
authSecret);
sub.setPrincipal(pushManager._principal);
this.resolve(sub);
},
_getKey: function(subscription, name) {
let outKeyLen = {};
let rawKey = subscription.getKey(name, outKeyLen);
if (!outKeyLen.value) {
return null;
}
let key = new ArrayBuffer(outKeyLen.value);
let keyView = new Uint8Array(key);
keyView.set(rawKey);
return key;
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Push]);

View File

@ -2,13 +2,10 @@
component {cde1d019-fad8-4044-b141-65fb4fb7a245} Push.js
contract @mozilla.org/push/PushManager;1 {cde1d019-fad8-4044-b141-65fb4fb7a245}
# XPCOM component; initializes the PushService on startup.
component {32028e38-903b-4a64-a180-5857eb4cb3dd} PushNotificationService.js
contract @mozilla.org/push/NotificationService;1 {32028e38-903b-4a64-a180-5857eb4cb3dd}
category app-startup PushNotificationService @mozilla.org/push/NotificationService;1
# XPCOM components.
component {daaa8d73-677e-4233-8acd-2c404bd01658} PushComponents.js
contract @mozilla.org/push/Service;1 {daaa8d73-677e-4233-8acd-2c404bd01658}
category app-startup PushServiceParent @mozilla.org/push/Service;1
component {66a87970-6dc9-46e0-ac61-adb4a13791de} PushNotificationService.js
contract @mozilla.org/push/ObserverNotification;1 {66a87970-6dc9-46e0-ac61-adb4a13791de}
component {16042199-bec0-484a-9640-25ecc0c0a149} PushClient.js
contract @mozilla.org/push/PushClient;1 {16042199-bec0-484a-9640-25ecc0c0a149}
component {e68997fd-8b92-49ee-af12-800830b023e8} PushComponents.js
contract @mozilla.org/push/ObserverNotification;1 {e68997fd-8b92-49ee-af12-800830b023e8}

View File

@ -1,167 +0,0 @@
/* 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/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "console", () => {
let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
maxLogLevelPref: "dom.push.loglevel",
prefix: "PushClient",
});
});
const kMessages = [
"PushService:Register:OK",
"PushService:Register:KO",
"PushService:Registration:OK",
"PushService:Registration:KO",
"PushService:Unregister:OK",
"PushService:Unregister:KO",
];
this.PushClient = function PushClient() {
console.debug("PushClient()");
this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
this._requests = {};
this.addListeners();
};
PushClient.prototype = {
classID: Components.ID("{16042199-bec0-484a-9640-25ecc0c0a149}"),
contractID: "@mozilla.org/push/PushClient;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIPushClient,
Ci.nsIMessageListener,]),
_getRandomId: function() {
return Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator).generateUUID().toString();
},
addRequest: function(data) {
let id = this._getRandomId();
this._requests[id] = data;
return id;
},
takeRequest: function(requestId) {
let d = this._requests[requestId];
delete this._requests[requestId];
return d;
},
addListeners: function() {
for (let message of kMessages) {
this._cpmm.addWeakMessageListener(message, this);
}
},
subscribe: function(scope, principal, callback) {
console.debug("subscribe()", scope);
let requestId = this.addRequest(callback);
this._cpmm.sendAsyncMessage("Push:Register", {
scope: scope,
requestID: requestId,
}, null, principal);
},
unsubscribe: function(scope, principal, callback) {
console.debug("unsubscribe()", scope);
let requestId = this.addRequest(callback);
this._cpmm.sendAsyncMessage("Push:Unregister", {
scope: scope,
requestID: requestId,
}, null, principal);
},
getSubscription: function(scope, principal, callback) {
console.debug("getSubscription()", scope);
let requestId = this.addRequest(callback);
console.debug("getSubscription: Going to send", scope, principal,
requestId);
this._cpmm.sendAsyncMessage("Push:Registration", {
scope: scope,
requestID: requestId,
}, null, principal);
},
_deliverPushEndpoint: function(request, registration) {
if (!registration) {
request.onPushEndpoint(Cr.NS_OK, "", 0, null, 0, null);
return;
}
let key;
if (registration.p256dhKey) {
key = new Uint8Array(registration.p256dhKey);
}
let authSecret;
if (registration.authSecret) {
authSecret = new Uint8Array(registration.authSecret);
}
request.onPushEndpoint(Cr.NS_OK,
registration.pushEndpoint,
key ? key.length : 0,
key,
authSecret ? authSecret.length : 0,
authSecret);
},
receiveMessage: function(aMessage) {
console.debug("receiveMessage()", aMessage);
let json = aMessage.data;
let request = this.takeRequest(json.requestID);
if (!request) {
console.error("receiveMessage: Unknown request ID", json.requestID);
return;
}
switch (aMessage.name) {
case "PushService:Register:OK":
case "PushService:Registration:OK":
this._deliverPushEndpoint(request, json.result);
break;
case "PushService:Register:KO":
case "PushService:Registration:KO":
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null, 0, null);
break;
case "PushService:Unregister:OK":
if (typeof json.result !== "boolean") {
console.error("receiveMessage: Expected boolean for unregister response",
json.result);
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
return;
}
request.onUnsubscribe(Cr.NS_OK, json.result);
break;
case "PushService:Unregister:KO":
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
break;
default:
console.error("receiveMessage: NOT IMPLEMENTED!", aMessage.name);
}
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PushClient]);

449
dom/push/PushComponents.js Normal file
View File

@ -0,0 +1,449 @@
/* 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/. */
"use strict";
/**
* This file exports XPCOM components for C++ and chrome JavaScript callers to
* interact with the Push service.
*/
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var isParent = Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
/**
* `PushServiceBase`, `PushServiceParent`, and `PushServiceContent` collectively
* implement the `nsIPushService` interface. This interface provides calls
* similar to the Push DOM API, but does not require service workers.
*
* Push service methods may be called from the parent or content process. The
* parent process implementation loads `PushService.jsm` at app startup, and
* calls its methods directly. The content implementation forwards calls to
* the parent Push service via IPC.
*
* The implementations share a class and contract ID.
*/
function PushServiceBase() {
this.wrappedJSObject = this;
this._addListeners();
}
PushServiceBase.prototype = {
classID: Components.ID("{daaa8d73-677e-4233-8acd-2c404bd01658}"),
contractID: "@mozilla.org/push/Service;1",
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
Ci.nsIPushService,
Ci.nsIPushQuotaManager,
]),
_handleReady() {},
_addListeners() {
for (let message of this._messages) {
this._mm.addMessageListener(message, this);
}
},
_isValidMessage(message) {
return this._messages.includes(message.name);
},
observe(subject, topic, data) {
if (topic === "app-startup") {
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
return;
}
if (topic === "sessionstore-windows-restored") {
Services.obs.removeObserver(this, "sessionstore-windows-restored");
this._handleReady();
return;
}
},
_deliverSubscription(request, props) {
if (!props) {
request.onPushSubscription(Cr.NS_OK, null);
return;
}
request.onPushSubscription(Cr.NS_OK, new PushSubscription(props));
},
};
/**
* The parent process implementation of `nsIPushService`. This version loads
* `PushService.jsm` at startup and calls its methods directly. It also
* receives and responds to requests from the content process.
*/
function PushServiceParent() {
PushServiceBase.call(this);
}
PushServiceParent.prototype = Object.create(PushServiceBase.prototype);
XPCOMUtils.defineLazyServiceGetter(PushServiceParent.prototype, "_mm",
"@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
XPCOMUtils.defineLazyGetter(PushServiceParent.prototype, "_service",
function() {
const {PushService} = Cu.import("resource://gre/modules/PushService.jsm",
{});
PushService.init();
return PushService;
});
Object.assign(PushServiceParent.prototype, {
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceParent),
_messages: [
"Push:Register",
"Push:Registration",
"Push:Unregister",
"Push:RegisterEventNotificationListener",
"Push:NotificationForOriginShown",
"Push:NotificationForOriginClosed",
"child-process-shutdown",
],
// nsIPushService methods
subscribe(scope, principal, callback) {
return this._handleRequest("Push:Register", principal, {
scope: scope,
}).then(result => {
this._deliverSubscription(callback, result);
}, error => {
callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null);
}).catch(Cu.reportError);
},
unsubscribe(scope, principal, callback) {
this._handleRequest("Push:Unregister", principal, {
scope: scope,
}).then(result => {
callback.onUnsubscribe(Cr.NS_OK, result);
}, error => {
callback.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
}).catch(Cu.reportError);
},
getSubscription(scope, principal, callback) {
return this._handleRequest("Push:Registration", principal, {
scope: scope,
}).then(result => {
this._deliverSubscription(callback, result);
}, error => {
callback.onPushSubscription(Cr.NS_ERROR_FAILURE, null);
}).catch(Cu.reportError);
},
// nsIPushQuotaManager methods
notificationForOriginShown(origin) {
this._service.notificationForOriginShown(origin);
},
notificationForOriginClosed(origin) {
this._service.notificationForOriginClosed(origin);
},
receiveMessage(message) {
if (!this._isValidMessage(message)) {
return;
}
let {name, principal, target, data} = message;
if (name === "Push:RegisterEventNotificationListener") {
this._service.registerListener(target);
return;
}
if (name === "child-process-shutdown") {
this._service.unregisterListener(target);
return;
}
if (name === "Push:NotificationForOriginShown") {
this.notificationForOriginShown(data);
return;
}
if (name === "Push:NotificationForOriginClosed") {
this.notificationForOriginClosed(data);
return;
}
if (!target.assertPermission("push")) {
return;
}
let sender = target.QueryInterface(Ci.nsIMessageSender);
return this._handleRequest(name, principal, data).then(result => {
sender.sendAsyncMessage(this._getResponseName(name, "OK"), {
requestID: data.requestID,
result: result
});
}, error => {
sender.sendAsyncMessage(this._getResponseName(name, "KO"), {
requestID: data.requestID,
});
}).catch(Cu.reportError);
},
_handleReady() {
this._service.init();
},
_toPageRecord(principal, data) {
if (!data.scope) {
throw new Error("Invalid page record: missing scope");
}
data.originAttributes =
ChromeUtils.originAttributesToSuffix(principal.originAttributes);
return data;
},
_handleRequest(name, principal, data) {
if (!principal) {
return Promise.reject(new Error("Invalid request: missing principal"));
}
let pageRecord;
try {
pageRecord = this._toPageRecord(principal, data);
} catch (e) {
return Promise.reject(e);
}
if (name === "Push:Register") {
return this._service.register(pageRecord);
}
if (name === "Push:Registration") {
return this._service.registration(pageRecord);
}
if (name === "Push:Unregister") {
return this._service.unregister(pageRecord);
}
return Promise.reject(new Error("Invalid request: unknown name"));
},
_getResponseName(requestName, suffix) {
let name = requestName.slice("Push:".length);
return "PushService:" + name + ":" + suffix;
},
});
/**
* The content process implementation of `nsIPushService`. This version
* uses the child message manager to forward calls to the parent process.
* The parent Push service instance handles the request, and responds with a
* message containing the result.
*/
function PushServiceContent() {
PushServiceBase.apply(this, arguments);
this._requests = new Map();
this._requestId = 0;
}
PushServiceContent.prototype = Object.create(PushServiceBase.prototype);
XPCOMUtils.defineLazyServiceGetter(PushServiceContent.prototype,
"_mm", "@mozilla.org/childprocessmessagemanager;1",
"nsISyncMessageSender");
Object.assign(PushServiceContent.prototype, {
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceContent),
_messages: [
"PushService:Register:OK",
"PushService:Register:KO",
"PushService:Registration:OK",
"PushService:Registration:KO",
"PushService:Unregister:OK",
"PushService:Unregister:KO",
],
// nsIPushService methods
subscribe(scope, principal, callback) {
let requestId = this._addRequest(callback);
this._mm.sendAsyncMessage("Push:Register", {
scope: scope,
requestID: requestId,
}, null, principal);
},
unsubscribe(scope, principal, callback) {
let requestId = this._addRequest(callback);
this._mm.sendAsyncMessage("Push:Unregister", {
scope: scope,
requestID: requestId,
}, null, principal);
},
getSubscription(scope, principal, callback) {
let requestId = this._addRequest(callback);
this._mm.sendAsyncMessage("Push:Registration", {
scope: scope,
requestID: requestId,
}, null, principal);
},
// nsIPushQuotaManager methods
notificationForOriginShown(origin) {
this._mm.sendAsyncMessage("Push:NotificationForOriginShown", origin);
},
notificationForOriginClosed(origin) {
this._mm.sendAsyncMessage("Push:NotificationForOriginClosed", origin);
},
_addRequest(data) {
let id = ++this._requestId;
this._requests.set(id, data);
return id;
},
_takeRequest(requestId) {
let d = this._requests.get(requestId);
this._requests.delete(requestId);
return d;
},
receiveMessage(message) {
if (!this._isValidMessage(message)) {
return;
}
let {name, data} = message;
let request = this._takeRequest(data.requestID);
if (!request) {
return;
}
switch (name) {
case "PushService:Register:OK":
case "PushService:Registration:OK":
this._deliverSubscription(request, data.result);
break;
case "PushService:Register:KO":
case "PushService:Registration:KO":
request.onPushSubscription(Cr.NS_ERROR_FAILURE, null);
break;
case "PushService:Unregister:OK":
if (typeof data.result === "boolean") {
request.onUnsubscribe(Cr.NS_OK, data.result);
} else {
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
}
break;
case "PushService:Unregister:KO":
request.onUnsubscribe(Cr.NS_ERROR_FAILURE, false);
break;
default:
break;
}
},
});
/** `PushSubscription` instances are passed to all subscription callbacks. */
function PushSubscription(props) {
this._props = props;
}
PushSubscription.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.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 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, outKeyLen) {
if (name === "p256dh") {
return this._getRawKey(this._props.p256dhKey, outKeyLen);
}
if (name === "auth") {
return this._getRawKey(this._props.authenticationSecret, outKeyLen);
}
return null;
},
_getRawKey(key, outKeyLen) {
if (!key) {
return null;
}
let rawKey = new Uint8Array(key);
if (outKeyLen) {
outKeyLen.value = rawKey.length;
}
return rawKey;
},
};
/**
* `PushObserverNotification` instances are passed to all
* `push-notification` observers.
*/
function PushObserverNotification() {}
PushObserverNotification.prototype = {
classID: Components.ID("{e68997fd-8b92-49ee-af12-800830b023e8}"),
contractID: "@mozilla.org/push/ObserverNotification;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushObserverNotification]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
PushObserverNotification,
// Export the correct implementation depending on whether we're running in
// the parent or content process.
isParent ? PushServiceParent : PushServiceContent,
]);

View File

@ -19,7 +19,7 @@
#include "nsIGlobalObject.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIPushClient.h"
#include "nsIPushService.h"
#include "nsComponentManagerUtils.h"
#include "nsFrameMessageManager.h"
@ -104,9 +104,9 @@ PushSubscription::Unsubscribe(ErrorResult& aRv)
{
MOZ_ASSERT(mPrincipal);
nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1");
if (NS_WARN_IF(!client)) {
nsCOMPtr<nsIPushService> service =
do_GetService("@mozilla.org/push/Service;1");
if (NS_WARN_IF(!service)) {
aRv = NS_ERROR_FAILURE;
return nullptr;
}
@ -118,7 +118,8 @@ PushSubscription::Unsubscribe(ErrorResult& aRv)
RefPtr<UnsubscribeResultCallback> callback =
new UnsubscribeResultCallback(p);
client->Unsubscribe(mScope, mPrincipal, callback);
Unused << NS_WARN_IF(NS_FAILED(
service->Unsubscribe(mScope, mPrincipal, callback)));
return p.forget();
}
@ -457,15 +458,15 @@ public:
RefPtr<WorkerUnsubscribeResultCallback> callback =
new WorkerUnsubscribeResultCallback(mProxy);
nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1");
if (!client) {
nsCOMPtr<nsIPushService> service =
do_GetService("@mozilla.org/push/Service;1");
if (!service) {
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
return NS_OK;
}
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
if (NS_WARN_IF(NS_FAILED(client->Unsubscribe(mScope, principal, callback)))) {
if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
return NS_OK;
}
@ -578,7 +579,7 @@ private:
nsTArray<uint8_t> mAuthSecret;
};
class GetSubscriptionCallback final : public nsIPushEndpointCallback
class GetSubscriptionCallback final : public nsIPushSubscriptionCallback
{
public:
NS_DECL_ISUPPORTS
@ -590,15 +591,11 @@ public:
{}
NS_IMETHOD
OnPushEndpoint(nsresult aStatus,
const nsAString& aEndpoint,
uint32_t aKeyLen,
uint8_t* aKey,
uint32_t aAuthSecretLen,
uint8_t* aAuthSecret) override
OnPushSubscription(nsresult aStatus,
nsIPushSubscription* aSubscription) override
{
AssertIsOnMainThread();
MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?");
RefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
@ -610,17 +607,17 @@ public:
AutoJSAPI jsapi;
jsapi.Init();
nsTArray<uint8_t> rawP256dhKey(aKeyLen);
rawP256dhKey.ReplaceElementsAt(0, aKeyLen, aKey, aKeyLen);
nsTArray<uint8_t> authSecret(aAuthSecretLen);
authSecret.ReplaceElementsAt(0, aAuthSecretLen,
aAuthSecret, aAuthSecretLen);
nsAutoString endpoint;
nsTArray<uint8_t> rawP256dhKey, authSecret;
if (NS_SUCCEEDED(aStatus)) {
aStatus = GetSubscriptionParams(aSubscription, endpoint, rawP256dhKey,
authSecret);
}
RefPtr<GetSubscriptionResultRunnable> r =
new GetSubscriptionResultRunnable(proxy,
aStatus,
aEndpoint,
endpoint,
mScope,
rawP256dhKey,
authSecret);
@ -630,23 +627,73 @@ public:
// Convenience method for use in this file.
void
OnPushEndpointError(nsresult aStatus)
OnPushSubscriptionError(nsresult aStatus)
{
Unused << NS_WARN_IF(NS_FAILED(
OnPushEndpoint(aStatus, EmptyString(), 0, nullptr, 0, nullptr)));
OnPushSubscription(aStatus, nullptr)));
}
protected:
~GetSubscriptionCallback()
{}
private:
inline nsresult
FreeKeys(nsresult aStatus, uint8_t* aKey, uint8_t* aAuthSecret)
{
NS_Free(aKey);
NS_Free(aAuthSecret);
return aStatus;
}
nsresult
GetSubscriptionParams(nsIPushSubscription* aSubscription,
nsAString& aEndpoint,
nsTArray<uint8_t>& aRawP256dhKey,
nsTArray<uint8_t>& aAuthSecret)
{
if (!aSubscription) {
return NS_OK;
}
nsresult rv = aSubscription->GetEndpoint(aEndpoint);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
uint8_t* key = nullptr;
uint8_t* authSecret = nullptr;
uint32_t keyLen;
rv = aSubscription->GetKey(NS_LITERAL_STRING("p256dh"), &keyLen, &key);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FreeKeys(rv, key, authSecret);
}
uint32_t authSecretLen;
rv = aSubscription->GetKey(NS_LITERAL_STRING("auth"), &authSecretLen,
&authSecret);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FreeKeys(rv, key, authSecret);
}
if (!aRawP256dhKey.SetLength(keyLen, fallible) ||
!aRawP256dhKey.ReplaceElementsAt(0, keyLen, key, keyLen, fallible) ||
!aAuthSecret.SetLength(authSecretLen, fallible) ||
!aAuthSecret.ReplaceElementsAt(0, authSecretLen, authSecret,
authSecretLen, fallible)) {
return FreeKeys(NS_ERROR_OUT_OF_MEMORY, key, authSecret);
}
return FreeKeys(NS_OK, key, authSecret);
}
RefPtr<PromiseWorkerProxy> mProxy;
nsString mScope;
};
NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback)
NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushSubscriptionCallback)
class GetSubscriptionRunnable final : public nsRunnable
{
@ -674,35 +721,35 @@ public:
PushPermissionState state;
nsresult rv = GetPermissionState(principal, state);
if (NS_FAILED(rv)) {
callback->OnPushEndpointError(NS_ERROR_FAILURE);
callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
return NS_OK;
}
if (state != PushPermissionState::Granted) {
if (mAction == WorkerPushManager::GetSubscriptionAction) {
callback->OnPushEndpointError(NS_OK);
callback->OnPushSubscriptionError(NS_OK);
return NS_OK;
}
callback->OnPushEndpointError(NS_ERROR_FAILURE);
callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
return NS_OK;
}
nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1");
if (!client) {
callback->OnPushEndpointError(NS_ERROR_FAILURE);
nsCOMPtr<nsIPushService> service =
do_GetService("@mozilla.org/push/Service;1");
if (!service) {
callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
return NS_OK;
}
if (mAction == WorkerPushManager::SubscribeAction) {
rv = client->Subscribe(mScope, principal, callback);
rv = service->Subscribe(mScope, principal, callback);
} else {
MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction);
rv = client->GetSubscription(mScope, principal, callback);
rv = service->GetSubscription(mScope, principal, callback);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->OnPushEndpointError(NS_ERROR_FAILURE);
callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
return NS_OK;
}

View File

@ -1,114 +0,0 @@
/* 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/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var isParent = Cc["@mozilla.org/xre/runtime;1"]
.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
XPCOMUtils.defineLazyGetter(this, "PushService", function() {
// Lazily initialize the PushService on
// `sessionstore-windows-restored` or first use.
const {PushService} = Cu.import("resource://gre/modules/PushService.jsm", {});
if (isParent) {
PushService.init();
}
return PushService;
});
this.PushNotificationService = function PushNotificationService() {};
PushNotificationService.prototype = {
classID: Components.ID("{32028e38-903b-4a64-a180-5857eb4cb3dd}"),
contractID: "@mozilla.org/push/NotificationService;1",
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference,
Ci.nsIPushNotificationService,
Ci.nsIPushQuotaManager,]),
register: function register(scope, originAttributes) {
return PushService.register({
scope: scope,
originAttributes: originAttributes,
maxQuota: Infinity,
});
},
unregister: function unregister(scope, originAttributes) {
return PushService.unregister({scope, originAttributes});
},
registration: function registration(scope, originAttributes) {
return PushService.registration({scope, originAttributes});
},
clearAll: function clearAll() {
return PushService._clearAll();
},
clearForDomain: function(domain) {
return PushService._clearForDomain(domain);
},
observe: function observe(subject, topic, data) {
switch (topic) {
case "app-startup":
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
break;
case "sessionstore-windows-restored":
Services.obs.removeObserver(this, "sessionstore-windows-restored");
if (isParent) {
PushService.init();
}
break;
}
},
// nsIPushQuotaManager methods
notificationForOriginShown: function(origin) {
if (!isParent) {
Services.cpmm.sendAsyncMessage("Push:NotificationForOriginShown", origin);
return;
}
PushService._notificationForOriginShown(origin);
},
notificationForOriginClosed: function(origin) {
if (!isParent) {
Services.cpmm.sendAsyncMessage("Push:NotificationForOriginClosed", origin);
return;
}
PushService._notificationForOriginClosed(origin);
}
};
this.PushObserverNotification = function PushObserverNotification() {};
PushObserverNotification.prototype = {
classID: Components.ID("{66a87970-6dc9-46e0-ac61-adb4a13791de}"),
contractID: "@mozilla.org/push/ObserverNotification;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushObserverNotification])
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
PushNotificationService,
PushObserverNotification
]);

View File

@ -28,6 +28,9 @@ this.EXPORTED_SYMBOLS = ["PushRecord"];
const prefs = new Preferences("dom.push.");
/**
* The push subscription record, stored in IndexedDB.
*/
function PushRecord(props) {
this.pushEndpoint = props.pushEndpoint;
this.scope = props.scope;
@ -217,11 +220,12 @@ PushRecord.prototype = {
toSubscription() {
return {
pushEndpoint: this.pushEndpoint,
endpoint: this.pushEndpoint,
lastPush: this.lastPush,
pushCount: this.pushCount,
p256dhKey: this.p256dhPublicKey,
authenticationSecret: this.authenticationSecret,
quota: this.quotaApplies() ? this.quota : -1,
};
},
};

View File

@ -43,12 +43,6 @@ XPCOMUtils.defineLazyGetter(this, "console", () => {
const prefs = new Preferences("dom.push.");
const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister",
"Push:Registration", "Push:RegisterEventNotificationListener",
"Push:NotificationForOriginShown",
"Push:NotificationForOriginClosed",
"child-process-shutdown"];
const PUSH_SERVICE_UNINIT = 0;
const PUSH_SERVICE_INIT = 1; // No serverURI
const PUSH_SERVICE_ACTIVATING = 2;//activating db
@ -105,7 +99,7 @@ this.PushService = {
// reduce the quota for a record. Used for testing purposes.
_updateQuotaTestCallback: null,
_childListeners: [],
_childListeners: new Set(),
// When serverURI changes (this is used for testing), db is cleaned up and a
// a new db is started. This events must be sequential.
@ -379,7 +373,7 @@ this.PushService = {
this._setState(PUSH_SERVICE_INIT);
return Promise.resolve();
}
return this._startService(service, uri, event)
return this._startService(service, uri)
.then(_ => this._stateChangeProcessEnqueue(_ =>
this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled")))
);
@ -390,7 +384,7 @@ this.PushService = {
if (this._state == PUSH_SERVICE_INIT) {
this._setState(PUSH_SERVICE_ACTIVATING);
// The service has not been running - start it.
return this._startService(service, uri, STARTING_SERVICE_EVENT)
return this._startService(service, uri)
.then(_ => this._stateChangeProcessEnqueue(_ =>
this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled")))
);
@ -402,7 +396,7 @@ this.PushService = {
// check is called in changeStateConnectionEnabledEvent function)
return this._stopService(CHANGING_SERVICE_EVENT)
.then(_ =>
this._startService(service, uri, CHANGING_SERVICE_EVENT)
this._startService(service, uri)
)
.then(_ => this._stateChangeProcessEnqueue(_ =>
this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled")))
@ -458,7 +452,7 @@ this.PushService = {
}
// Start service.
this._startService(service, uri, false, options).then(_ => {
this._startService(service, uri, options).then(_ => {
// Before completing the activation check prefs. This will first check
// connection.enabled pref and then check offline state.
this._changeStateConnectionEnabledEvent(prefs.get("connection.enabled"));
@ -515,24 +509,13 @@ this.PushService = {
Services.obs.addObserver(this, "perm-changed", false);
},
_startService: function(service, serverURI, event, options = {}) {
_startService: function(service, serverURI, options = {}) {
console.debug("startService()");
if (this._state != PUSH_SERVICE_ACTIVATING) {
return;
}
if (event != CHANGING_SERVICE_EVENT) {
// if only serverURL is changed we can keep listening for broadcast
// messages and queue them.
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
kCHILD_PROCESS_MESSAGES.forEach(msgName =>
ppmm.addMessageListener(msgName, this)
);
}
this._service = service;
this._db = options.db;
@ -564,15 +547,6 @@ this.PushService = {
this.stopAlarm();
this._stopObservers();
if (event != CHANGING_SERVICE_EVENT) {
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
kCHILD_PROCESS_MESSAGES.forEach(
msgName => ppmm.removeMessageListener(msgName, this)
);
}
this._service.disconnect();
this._service.uninit();
this._service = null;
@ -616,7 +590,7 @@ this.PushService = {
uninit: function() {
console.debug("uninit()");
this._childListeners = [];
this._childListeners.clear();
if (this._state == PUSH_SERVICE_UNINIT) {
return;
@ -725,14 +699,14 @@ this.PushService = {
},
_notifyListeners: function(name, data) {
if (this._childListeners.length > 0) {
if (this._childListeners.size > 0) {
// Try to send messages to all listeners, but remove any that fail since
// the receiver is likely gone away.
for (var i = this._childListeners.length - 1; i >= 0; --i) {
for (let listener of this._childListeners) {
try {
this._childListeners[i].sendAsyncMessage(name, data);
listener.sendAsyncMessage(name, data);
} catch(e) {
this._childListeners.splice(i, 1);
this._childListeners.delete(listener);
}
}
} else {
@ -944,7 +918,7 @@ this.PushService = {
});
},
_notificationForOriginShown(origin) {
notificationForOriginShown(origin) {
console.debug("notificationForOriginShown()", origin);
let count;
if (this._visibleNotifications.has(origin)) {
@ -955,7 +929,7 @@ this.PushService = {
this._visibleNotifications.set(origin, count + 1);
},
_notificationForOriginClosed(origin) {
notificationForOriginClosed(origin) {
console.debug("notificationForOriginClosed()", origin);
let count;
if (this._visibleNotifications.has(origin)) {
@ -1106,99 +1080,27 @@ this.PushService = {
throw reply.error;
},
receiveMessage: function(aMessage) {
console.debug("receiveMessage()", aMessage.name);
if (kCHILD_PROCESS_MESSAGES.indexOf(aMessage.name) == -1) {
console.debug("receiveMessage: Invalid message from child",
aMessage.name);
return;
}
if (aMessage.name === "Push:RegisterEventNotificationListener") {
console.debug("receiveMessage: Adding child listener");
this._childListeners.push(aMessage.target);
return;
}
if (aMessage.name === "child-process-shutdown") {
console.debug("receiveMessage: Possibly removing child listener");
for (var i = this._childListeners.length - 1; i >= 0; --i) {
if (this._childListeners[i] == aMessage.target) {
console.debug("receiveMessage: Removed child listener");
this._childListeners.splice(i, 1);
}
}
console.debug("receiveMessage: Clearing notifications from child");
this._visibleNotifications.clear();
return;
}
if (aMessage.name === "Push:NotificationForOriginShown") {
console.debug("receiveMessage: Notification shown from child");
this._notificationForOriginShown(aMessage.data);
return;
}
if (aMessage.name === "Push:NotificationForOriginClosed") {
console.debug("receiveMessage: Notification closed from child");
this._notificationForOriginClosed(aMessage.data);
return;
}
if (!aMessage.target.assertPermission("push")) {
console.debug("receiveMessage: Got message from a child process that",
"does not have 'push' permission");
return null;
}
let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender);
let name = aMessage.name.slice("Push:".length);
Promise.resolve().then(_ => {
let pageRecord = this._validatePageRecord(aMessage);
return this[name.toLowerCase()](pageRecord);
}).then(result => {
mm.sendAsyncMessage("PushService:" + name + ":OK", {
requestID: aMessage.data.requestID,
result: result,
});
}, error => {
console.error("receiveMessage: Error handling message", aMessage, error);
mm.sendAsyncMessage("PushService:" + name + ":KO", {
requestID: aMessage.data.requestID,
});
}).catch(error => {
console.error("receiveMessage: Error sending reply", error);
});
registerListener(listener) {
console.debug("registerListener: Adding child listener");
this._childListeners.add(listener);
},
_validatePageRecord: function(aMessage) {
let principal = aMessage.principal;
if (!principal) {
throw new Error("Missing message principal");
}
unregisterListener(listener) {
console.debug("unregisterListener: Possibly removing child listener");
this._childListeners.delete(listener);
this._visibleNotifications.clear();
},
let pageRecord = aMessage.data;
if (!pageRecord.scope) {
throw new Error("Missing page record scope");
}
pageRecord.originAttributes =
ChromeUtils.originAttributesToSuffix(principal.originAttributes);
return pageRecord;
_getByPageRecord(pageRecord) {
return this._checkActivated().then(_ =>
this._db.getByIdentifiers(pageRecord)
);
},
register: function(aPageRecord) {
console.debug("register()", aPageRecord);
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
return Promise.reject(new Error("Invalid page record"));
}
return this._checkActivated()
.then(_ => this._db.getByIdentifiers(aPageRecord))
return this._getByPageRecord(aPageRecord)
.then(record => {
if (!record) {
return this._lookupOrPutPendingRequest(aPageRecord);
@ -1245,12 +1147,7 @@ this.PushService = {
unregister: function(aPageRecord) {
console.debug("unregister()", aPageRecord);
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
return Promise.reject(new Error("Invalid page record"));
}
return this._checkActivated()
.then(_ => this._db.getByIdentifiers(aPageRecord))
return this._getByPageRecord(aPageRecord)
.then(record => {
if (record === undefined) {
return false;
@ -1313,12 +1210,8 @@ this.PushService = {
registration: function(aPageRecord) {
console.debug("registration()");
if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
return Promise.reject(new Error("Invalid page record"));
}
return this._checkActivated()
.then(_ => this._db.getByIdentifiers(aPageRecord))
return this._getByPageRecord(aPageRecord)
.then(record => {
if (!record) {
return null;

View File

@ -6,8 +6,7 @@
EXTRA_COMPONENTS += [
'Push.js',
'Push.manifest',
'PushClient.js',
'PushNotificationService.js',
'PushComponents.js',
]
EXTRA_JS_MODULES += [