зеркало из https://github.com/mozilla/gecko-dev.git
177 строки
6.1 KiB
JavaScript
177 строки
6.1 KiB
JavaScript
/* jshint moz: true, esnext: true */
|
|
/* 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/. */
|
|
|
|
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
const {EventDispatcher} = ChromeUtils.import("resource://gre/modules/Messaging.jsm");
|
|
const {
|
|
PushCrypto,
|
|
getCryptoParams,
|
|
} = ChromeUtils.import("resource://gre/modules/PushCrypto.jsm", null);
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "PushService",
|
|
"@mozilla.org/push/Service;1", "nsIPushService");
|
|
XPCOMUtils.defineLazyGetter(this, "_decoder", () => new TextDecoder());
|
|
|
|
const FXA_PUSH_SCOPE = "chrome://fxa-push";
|
|
const Log = ChromeUtils.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.bind("FxAccountsPush");
|
|
|
|
function FxAccountsPush() {
|
|
Services.obs.addObserver(this, "FxAccountsPush:ReceivedPushMessageToDecode");
|
|
|
|
EventDispatcher.instance.sendRequestForResult({
|
|
type: "FxAccountsPush:Initialized",
|
|
});
|
|
}
|
|
|
|
FxAccountsPush.prototype = {
|
|
observe: function(subject, topic, data) {
|
|
switch (topic) {
|
|
case "android-push-service":
|
|
if (data === "android-fxa-subscribe") {
|
|
this._subscribe();
|
|
} else if (data === "android-fxa-unsubscribe") {
|
|
this._unsubscribe();
|
|
} else if (data === "android-fxa-resubscribe") {
|
|
// If unsubscription fails, we still want to try to subscribe.
|
|
this._unsubscribe().then(this._subscribe, this._subscribe);
|
|
}
|
|
break;
|
|
case "FxAccountsPush:ReceivedPushMessageToDecode":
|
|
this._decodePushMessage(data);
|
|
break;
|
|
}
|
|
},
|
|
|
|
_subscribe() {
|
|
Log.i("FxAccountsPush _subscribe");
|
|
return new Promise((resolve, reject) => {
|
|
PushService.subscribe(FXA_PUSH_SCOPE,
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
(result, subscription) => {
|
|
if (Components.isSuccessCode(result)) {
|
|
Log.d("FxAccountsPush got subscription");
|
|
resolve(subscription);
|
|
} else {
|
|
Log.w("FxAccountsPush failed to subscribe", result);
|
|
const err = new Error("FxAccountsPush failed to subscribe");
|
|
err.result = result;
|
|
reject(err);
|
|
}
|
|
});
|
|
})
|
|
.then(subscription => {
|
|
EventDispatcher.instance.sendRequest({
|
|
type: "FxAccountsPush:Subscribe:Response",
|
|
subscription: {
|
|
pushCallback: subscription.endpoint,
|
|
pushPublicKey: urlsafeBase64Encode(subscription.getKey("p256dh")),
|
|
pushAuthKey: urlsafeBase64Encode(subscription.getKey("auth")),
|
|
},
|
|
});
|
|
})
|
|
.catch(err => {
|
|
Log.i("Error when registering FxA push endpoint " + err);
|
|
EventDispatcher.instance.sendRequest({
|
|
type: "FxAccountsPush:Subscribe:Response",
|
|
error: err.result.toString(), // Convert to string because the GeckoBundle can't getLong();
|
|
});
|
|
});
|
|
},
|
|
|
|
_unsubscribe() {
|
|
Log.i("FxAccountsPush _unsubscribe");
|
|
return new Promise((resolve) => {
|
|
PushService.unsubscribe(FXA_PUSH_SCOPE,
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
(result, ok) => {
|
|
if (Components.isSuccessCode(result)) {
|
|
if (ok === true) {
|
|
Log.d("FxAccountsPush unsubscribed");
|
|
} else {
|
|
Log.d("FxAccountsPush had no subscription to unsubscribe");
|
|
}
|
|
} else {
|
|
Log.w("FxAccountsPush failed to unsubscribe", result);
|
|
}
|
|
return resolve(ok);
|
|
});
|
|
}).catch(err => {
|
|
Log.e("Error during unsubscribe", err);
|
|
});
|
|
},
|
|
|
|
_decodePushMessage(data) {
|
|
Log.i("FxAccountsPush _decodePushMessage");
|
|
data = JSON.parse(data);
|
|
let { headers, message } = this._messageAndHeaders(data);
|
|
return new Promise((resolve, reject) => {
|
|
PushService.getSubscription(FXA_PUSH_SCOPE,
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
(result, subscription) => {
|
|
if (!Components.isSuccessCode(result)) {
|
|
return reject(new Error(`Error getting subscription (${result})`));
|
|
}
|
|
if (!subscription) {
|
|
return reject(new Error("No subscription found"));
|
|
}
|
|
return resolve(subscription);
|
|
});
|
|
}).then(subscription => {
|
|
return PushCrypto.decrypt(subscription.p256dhPrivateKey,
|
|
new Uint8Array(subscription.getKey("p256dh")),
|
|
new Uint8Array(subscription.getKey("auth")),
|
|
headers, message);
|
|
})
|
|
.then(plaintext => {
|
|
let decryptedMessage = plaintext ? _decoder.decode(plaintext) : "";
|
|
EventDispatcher.instance.sendRequestForResult({
|
|
type: "FxAccountsPush:ReceivedPushMessageToDecode:Response",
|
|
message: decryptedMessage,
|
|
});
|
|
})
|
|
.catch(err => {
|
|
Log.d("Error while decoding incoming message : " + err);
|
|
EventDispatcher.instance.sendRequestForResult({
|
|
type: "FxAccountsPush:ReceivedPushMessageToDecode:Response",
|
|
error: err.message || "",
|
|
});
|
|
});
|
|
},
|
|
|
|
// Copied from PushServiceAndroidGCM
|
|
_messageAndHeaders(data) {
|
|
// Default is no data (and no encryption).
|
|
let message = null;
|
|
let headers = null;
|
|
|
|
if (data.message && data.enc && (data.enckey || data.cryptokey)) {
|
|
headers = {
|
|
encryption_key: data.enckey,
|
|
crypto_key: data.cryptokey,
|
|
encryption: data.enc,
|
|
encoding: data.con,
|
|
};
|
|
// Ciphertext is (urlsafe) Base 64 encoded.
|
|
message = ChromeUtils.base64URLDecode(data.message, {
|
|
// The Push server may append padding.
|
|
padding: "ignore",
|
|
});
|
|
}
|
|
return { headers, message };
|
|
},
|
|
|
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
|
|
|
|
classID: Components.ID("{d1bbb0fd-1d47-4134-9c12-d7b1be20b721}"),
|
|
};
|
|
|
|
function urlsafeBase64Encode(key) {
|
|
return ChromeUtils.base64URLEncode(new Uint8Array(key), { pad: false });
|
|
}
|
|
|
|
var components = [ FxAccountsPush ];
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|