mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-13 02:37:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
558
dom/push/PushComponents.js
Normal file
558
dom/push/PushComponents.js
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
/* 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;
|
||||
|
||||
// The default Push service implementation.
|
||||
XPCOMUtils.defineLazyGetter(this, "PushService", function() {
|
||||
const {PushService} = Cu.import("resource://gre/modules/PushService.jsm",
|
||||
{});
|
||||
PushService.init();
|
||||
return PushService;
|
||||
});
|
||||
|
||||
// Observer notification topics for push messages and subscription status
|
||||
// changes. These are duplicated and used in `nsIPushNotifier`. They're exposed
|
||||
// on `nsIPushService` so that JS callers only need to import this service.
|
||||
const OBSERVER_TOPIC_PUSH = "push-message";
|
||||
const OBSERVER_TOPIC_SUBSCRIPTION_CHANGE = "push-subscription-change";
|
||||
const OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED = "push-subscription-modified";
|
||||
|
||||
/**
|
||||
* `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,
|
||||
Ci.nsIPushErrorReporter,
|
||||
]),
|
||||
|
||||
pushTopic: OBSERVER_TOPIC_PUSH,
|
||||
subscriptionChangeTopic: OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
|
||||
subscriptionModifiedTopic: OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
|
||||
|
||||
_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;
|
||||
}
|
||||
if (topic === "android-push-service") {
|
||||
// Load PushService immediately.
|
||||
this._handleReady();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_deliverSubscription(request, props) {
|
||||
if (!props) {
|
||||
request.onPushSubscription(Cr.NS_OK, null);
|
||||
return;
|
||||
}
|
||||
request.onPushSubscription(Cr.NS_OK, new PushSubscription(props));
|
||||
},
|
||||
|
||||
_deliverSubscriptionError(request, error) {
|
||||
let result = typeof error.result == "number" ?
|
||||
error.result : Cr.NS_ERROR_FAILURE;
|
||||
request.onPushSubscription(result, null);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 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");
|
||||
|
||||
Object.assign(PushServiceParent.prototype, {
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceParent),
|
||||
|
||||
_messages: [
|
||||
"Push:Register",
|
||||
"Push:Registration",
|
||||
"Push:Unregister",
|
||||
"Push:Clear",
|
||||
"Push:NotificationForOriginShown",
|
||||
"Push:NotificationForOriginClosed",
|
||||
"Push:ReportError",
|
||||
],
|
||||
|
||||
// nsIPushService methods
|
||||
|
||||
subscribe(scope, principal, callback) {
|
||||
this.subscribeWithKey(scope, principal, 0, null, callback);
|
||||
},
|
||||
|
||||
subscribeWithKey(scope, principal, keyLen, key, callback) {
|
||||
this._handleRequest("Push:Register", principal, {
|
||||
scope: scope,
|
||||
appServerKey: key,
|
||||
}).then(result => {
|
||||
this._deliverSubscription(callback, result);
|
||||
}, error => {
|
||||
this._deliverSubscriptionError(callback, error);
|
||||
}).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 => {
|
||||
this._deliverSubscriptionError(callback, error);
|
||||
}).catch(Cu.reportError);
|
||||
},
|
||||
|
||||
clearForDomain(domain, callback) {
|
||||
return this._handleRequest("Push:Clear", null, {
|
||||
domain: domain,
|
||||
}).then(result => {
|
||||
callback.onClear(Cr.NS_OK);
|
||||
}, error => {
|
||||
callback.onClear(Cr.NS_ERROR_FAILURE);
|
||||
}).catch(Cu.reportError);
|
||||
},
|
||||
|
||||
// nsIPushQuotaManager methods
|
||||
|
||||
notificationForOriginShown(origin) {
|
||||
this.service.notificationForOriginShown(origin);
|
||||
},
|
||||
|
||||
notificationForOriginClosed(origin) {
|
||||
this.service.notificationForOriginClosed(origin);
|
||||
},
|
||||
|
||||
// nsIPushErrorReporter methods
|
||||
|
||||
reportDeliveryError(messageId, reason) {
|
||||
this.service.reportDeliveryError(messageId, reason);
|
||||
},
|
||||
|
||||
receiveMessage(message) {
|
||||
if (!this._isValidMessage(message)) {
|
||||
return;
|
||||
}
|
||||
let {name, principal, target, data} = message;
|
||||
if (name === "Push:NotificationForOriginShown") {
|
||||
this.notificationForOriginShown(data);
|
||||
return;
|
||||
}
|
||||
if (name === "Push:NotificationForOriginClosed") {
|
||||
this.notificationForOriginClosed(data);
|
||||
return;
|
||||
}
|
||||
if (!target.assertPermission("push")) {
|
||||
return;
|
||||
}
|
||||
if (name === "Push:ReportError") {
|
||||
this.reportDeliveryError(data.messageId, data.reason);
|
||||
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,
|
||||
result: error.result,
|
||||
});
|
||||
}).catch(Cu.reportError);
|
||||
},
|
||||
|
||||
_handleReady() {
|
||||
this.service.init();
|
||||
},
|
||||
|
||||
_toPageRecord(principal, data) {
|
||||
if (!data.scope) {
|
||||
throw new Error("Invalid page record: missing scope");
|
||||
}
|
||||
if (!principal) {
|
||||
throw new Error("Invalid page record: missing principal");
|
||||
}
|
||||
if (principal.isNullPrincipal || principal.isExpandedPrincipal) {
|
||||
throw new Error("Invalid page record: unsupported principal");
|
||||
}
|
||||
|
||||
// System subscriptions can only be created by chrome callers, and are
|
||||
// exempt from the background message quota and permission checks. They
|
||||
// also do not fire service worker events.
|
||||
data.systemRecord = principal.isSystemPrincipal;
|
||||
|
||||
data.originAttributes =
|
||||
ChromeUtils.originAttributesToSuffix(principal.originAttributes);
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
_handleRequest(name, principal, data) {
|
||||
if (name == "Push:Clear") {
|
||||
return this.service.clear(data);
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
// Methods used for mocking in tests.
|
||||
|
||||
replaceServiceBackend(options) {
|
||||
return this.service.changeTestServer(options.serverURI, options);
|
||||
},
|
||||
|
||||
restoreServiceBackend() {
|
||||
var defaultServerURL = Services.prefs.getCharPref("dom.push.serverURL");
|
||||
return this.service.changeTestServer(defaultServerURL);
|
||||
},
|
||||
});
|
||||
|
||||
// Used to replace the implementation with a mock.
|
||||
Object.defineProperty(PushServiceParent.prototype, "service", {
|
||||
get() {
|
||||
return this._service || PushService;
|
||||
},
|
||||
set(impl) {
|
||||
this._service = impl;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 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",
|
||||
"PushService:Clear:OK",
|
||||
"PushService:Clear:KO",
|
||||
],
|
||||
|
||||
// nsIPushService methods
|
||||
|
||||
subscribe(scope, principal, callback) {
|
||||
this.subscribeWithKey(scope, principal, 0, null, callback);
|
||||
},
|
||||
|
||||
subscribeWithKey(scope, principal, keyLen, key, callback) {
|
||||
let requestId = this._addRequest(callback);
|
||||
this._mm.sendAsyncMessage("Push:Register", {
|
||||
scope: scope,
|
||||
appServerKey: key,
|
||||
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);
|
||||
},
|
||||
|
||||
clearForDomain(domain, callback) {
|
||||
let requestId = this._addRequest(callback);
|
||||
this._mm.sendAsyncMessage("Push:Clear", {
|
||||
domain: domain,
|
||||
requestID: requestId,
|
||||
});
|
||||
},
|
||||
|
||||
// nsIPushQuotaManager methods
|
||||
|
||||
notificationForOriginShown(origin) {
|
||||
this._mm.sendAsyncMessage("Push:NotificationForOriginShown", origin);
|
||||
},
|
||||
|
||||
notificationForOriginClosed(origin) {
|
||||
this._mm.sendAsyncMessage("Push:NotificationForOriginClosed", origin);
|
||||
},
|
||||
|
||||
// nsIPushErrorReporter methods
|
||||
|
||||
reportDeliveryError(messageId, reason) {
|
||||
this._mm.sendAsyncMessage("Push:ReportError", {
|
||||
messageId: messageId,
|
||||
reason: reason,
|
||||
});
|
||||
},
|
||||
|
||||
_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":
|
||||
this._deliverSubscriptionError(request, data);
|
||||
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;
|
||||
|
||||
case "PushService:Clear:OK":
|
||||
request.onClear(Cr.NS_OK);
|
||||
break;
|
||||
|
||||
case "PushService:Clear:KO":
|
||||
request.onClear(Cr.NS_ERROR_FAILURE);
|
||||
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 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, outKeyLen) {
|
||||
switch (name) {
|
||||
case "p256dh":
|
||||
return this._getRawKey(this._props.p256dhKey, outKeyLen);
|
||||
|
||||
case "auth":
|
||||
return this._getRawKey(this._props.authenticationSecret, outKeyLen);
|
||||
|
||||
case "appServer":
|
||||
return this._getRawKey(this._props.appServerKey, outKeyLen);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_getRawKey(key, outKeyLen) {
|
||||
if (!key) {
|
||||
return null;
|
||||
}
|
||||
let rawKey = new Uint8Array(key);
|
||||
if (outKeyLen) {
|
||||
outKeyLen.value = rawKey.length;
|
||||
}
|
||||
return rawKey;
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([
|
||||
// Export the correct implementation depending on whether we're running in
|
||||
// the parent or content process.
|
||||
isParent ? PushServiceParent : PushServiceContent,
|
||||
]);
|
||||
Loading…
Add table
Add a link
Reference in a new issue