2016-03-07 08:00:34 +03:00
|
|
|
/* 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/. */
|
|
|
|
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
ChromeUtils.import("resource://services-sync/util.js");
|
|
|
|
ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
|
2016-03-07 08:00:34 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* FxAccountsPushService manages Push notifications for Firefox Accounts in the browser
|
|
|
|
*
|
|
|
|
* @param [options]
|
|
|
|
* Object, custom options that used for testing
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function FxAccountsPushService(options = {}) {
|
|
|
|
this.log = log;
|
|
|
|
|
|
|
|
if (options.log) {
|
|
|
|
// allow custom log for testing purposes
|
|
|
|
this.log = options.log;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.log.debug("FxAccountsPush loading service");
|
|
|
|
this.wrappedJSObject = this;
|
|
|
|
this.initialize(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
FxAccountsPushService.prototype = {
|
|
|
|
/**
|
|
|
|
* Helps only initialize observers once.
|
|
|
|
*/
|
|
|
|
_initialized: false,
|
|
|
|
/**
|
|
|
|
* Instance of the nsIPushService or a mocked object.
|
|
|
|
*/
|
|
|
|
pushService: null,
|
|
|
|
/**
|
|
|
|
* Instance of FxAccounts or a mocked object.
|
|
|
|
*/
|
|
|
|
fxAccounts: null,
|
|
|
|
/**
|
|
|
|
* Component ID of this service, helps register this component.
|
|
|
|
*/
|
|
|
|
classID: Components.ID("{1b7db999-2ecd-4abf-bb95-a726896798ca}"),
|
|
|
|
/**
|
|
|
|
* Register used interfaces in this service
|
|
|
|
*/
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
|
|
|
/**
|
|
|
|
* Initialize the service and register all the required observers.
|
|
|
|
*
|
|
|
|
* @param [options]
|
|
|
|
*/
|
|
|
|
initialize(options) {
|
|
|
|
if (this._initialized) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._initialized = true;
|
|
|
|
|
|
|
|
if (options.pushService) {
|
|
|
|
this.pushService = options.pushService;
|
|
|
|
} else {
|
|
|
|
this.pushService = Cc["@mozilla.org/push/Service;1"].getService(Ci.nsIPushService);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.fxAccounts) {
|
|
|
|
this.fxAccounts = options.fxAccounts;
|
|
|
|
} else {
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.defineModuleGetter(this, "fxAccounts",
|
2016-03-07 08:00:34 +03:00
|
|
|
"resource://gre/modules/FxAccounts.jsm");
|
|
|
|
}
|
|
|
|
|
|
|
|
// listen to new push messages, push changes and logout events
|
2017-04-14 22:51:38 +03:00
|
|
|
Services.obs.addObserver(this, this.pushService.pushTopic);
|
|
|
|
Services.obs.addObserver(this, this.pushService.subscriptionChangeTopic);
|
|
|
|
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION);
|
2016-03-07 08:00:34 +03:00
|
|
|
|
|
|
|
this.log.debug("FxAccountsPush initialized");
|
2017-02-17 04:34:45 +03:00
|
|
|
return true;
|
2016-03-07 08:00:34 +03:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Registers a new endpoint with the Push Server
|
|
|
|
*
|
|
|
|
* @returns {Promise}
|
|
|
|
* Promise always resolves with a subscription or a null if failed to subscribe.
|
|
|
|
*/
|
|
|
|
registerPushEndpoint() {
|
|
|
|
this.log.trace("FxAccountsPush registerPushEndpoint");
|
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
this.pushService.subscribe(FXA_PUSH_SCOPE_ACCOUNT_UPDATE,
|
|
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
|
|
(result, subscription) => {
|
|
|
|
if (Components.isSuccessCode(result)) {
|
|
|
|
this.log.debug("FxAccountsPush got subscription");
|
|
|
|
resolve(subscription);
|
|
|
|
} else {
|
|
|
|
this.log.warn("FxAccountsPush failed to subscribe", result);
|
|
|
|
resolve(null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Standard observer interface to listen to push messages, changes and logout.
|
|
|
|
*
|
|
|
|
* @param subject
|
|
|
|
* @param topic
|
|
|
|
* @param data
|
2016-07-30 06:51:16 +03:00
|
|
|
* @returns {Promise}
|
2016-03-07 08:00:34 +03:00
|
|
|
*/
|
2016-07-30 06:51:16 +03:00
|
|
|
_observe(subject, topic, data) {
|
2016-06-08 20:39:57 +03:00
|
|
|
this.log.trace(`observed topic=${topic}, data=${data}, subject=${subject}`);
|
|
|
|
switch (topic) {
|
|
|
|
case this.pushService.pushTopic:
|
|
|
|
if (data === FXA_PUSH_SCOPE_ACCOUNT_UPDATE) {
|
|
|
|
let message = subject.QueryInterface(Ci.nsIPushMessage);
|
2017-02-17 04:34:45 +03:00
|
|
|
this._onPushMessage(message);
|
2016-06-08 20:39:57 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case this.pushService.subscriptionChangeTopic:
|
|
|
|
if (data === FXA_PUSH_SCOPE_ACCOUNT_UPDATE) {
|
2017-02-17 04:34:45 +03:00
|
|
|
this._onPushSubscriptionChange();
|
2016-06-08 20:39:57 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ONLOGOUT_NOTIFICATION:
|
|
|
|
// user signed out, we need to stop polling the Push Server
|
2017-02-17 04:34:45 +03:00
|
|
|
this.unsubscribe().catch(err => {
|
2016-06-08 20:39:57 +03:00
|
|
|
this.log.error("Error during unsubscribe", err);
|
|
|
|
});
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 08:00:34 +03:00
|
|
|
},
|
2016-07-30 06:51:16 +03:00
|
|
|
/**
|
|
|
|
* Wrapper around _observe that catches errors
|
|
|
|
*/
|
|
|
|
observe(subject, topic, data) {
|
|
|
|
Promise.resolve()
|
|
|
|
.then(() => this._observe(subject, topic, data))
|
|
|
|
.catch(err => this.log.error(err));
|
|
|
|
},
|
2016-03-07 08:00:34 +03:00
|
|
|
/**
|
|
|
|
* Fired when the Push server sends a notification.
|
|
|
|
*
|
|
|
|
* @private
|
2016-07-30 06:51:16 +03:00
|
|
|
* @returns {Promise}
|
2016-03-07 08:00:34 +03:00
|
|
|
*/
|
2016-06-08 20:39:57 +03:00
|
|
|
_onPushMessage(message) {
|
2016-03-07 08:00:34 +03:00
|
|
|
this.log.trace("FxAccountsPushService _onPushMessage");
|
2016-06-08 20:39:57 +03:00
|
|
|
if (!message.data) {
|
|
|
|
// Use the empty signal to check the verification state of the account right away
|
2016-09-27 10:42:12 +03:00
|
|
|
this.log.debug("empty push message - checking account status");
|
2017-02-17 04:34:45 +03:00
|
|
|
this.fxAccounts.checkVerificationStatus();
|
|
|
|
return;
|
2016-06-08 20:39:57 +03:00
|
|
|
}
|
|
|
|
let payload = message.data.json();
|
2016-09-27 10:42:12 +03:00
|
|
|
this.log.debug(`push command: ${payload.command}`);
|
2016-06-08 20:39:57 +03:00
|
|
|
switch (payload.command) {
|
2017-01-05 03:21:36 +03:00
|
|
|
case ON_DEVICE_CONNECTED_NOTIFICATION:
|
|
|
|
Services.obs.notifyObservers(null, ON_DEVICE_CONNECTED_NOTIFICATION, payload.data.deviceName);
|
|
|
|
break;
|
2016-06-08 20:40:42 +03:00
|
|
|
case ON_DEVICE_DISCONNECTED_NOTIFICATION:
|
2017-02-17 04:34:45 +03:00
|
|
|
this.fxAccounts.handleDeviceDisconnection(payload.data.id);
|
|
|
|
return;
|
2017-03-17 00:15:18 +03:00
|
|
|
case ON_PROFILE_UPDATED_NOTIFICATION:
|
|
|
|
// We already have a "profile updated" notification sent via WebChannel,
|
|
|
|
// let's just re-use that.
|
2017-04-14 22:51:39 +03:00
|
|
|
Services.obs.notifyObservers(null, ON_PROFILE_CHANGE_NOTIFICATION);
|
2017-03-17 00:15:18 +03:00
|
|
|
return;
|
2016-07-30 06:51:16 +03:00
|
|
|
case ON_PASSWORD_CHANGED_NOTIFICATION:
|
|
|
|
case ON_PASSWORD_RESET_NOTIFICATION:
|
2017-02-17 04:34:45 +03:00
|
|
|
this._onPasswordChanged();
|
|
|
|
return;
|
2017-05-24 02:22:49 +03:00
|
|
|
case ON_ACCOUNT_DESTROYED_NOTIFICATION:
|
|
|
|
this.fxAccounts.handleAccountDestroyed(payload.data.uid);
|
|
|
|
return;
|
2016-08-02 20:09:30 +03:00
|
|
|
case ON_COLLECTION_CHANGED_NOTIFICATION:
|
|
|
|
Services.obs.notifyObservers(null, ON_COLLECTION_CHANGED_NOTIFICATION, payload.data.collections);
|
2017-07-06 09:31:57 +03:00
|
|
|
return;
|
2017-06-23 00:04:07 +03:00
|
|
|
case ON_VERIFY_LOGIN_NOTIFICATION:
|
|
|
|
Services.obs.notifyObservers(null, ON_VERIFY_LOGIN_NOTIFICATION, JSON.stringify(payload.data));
|
|
|
|
break;
|
2016-06-08 20:39:57 +03:00
|
|
|
default:
|
|
|
|
this.log.warn("FxA Push command unrecognized: " + payload.command);
|
|
|
|
}
|
2016-03-07 08:00:34 +03:00
|
|
|
},
|
2016-07-30 06:51:16 +03:00
|
|
|
/**
|
|
|
|
* Check the FxA session status after a password change/reset event.
|
|
|
|
* If the session is invalid, reset credentials and notify listeners of
|
|
|
|
* ON_ACCOUNT_STATE_CHANGE_NOTIFICATION that the account may have changed
|
|
|
|
*
|
|
|
|
* @returns {Promise}
|
|
|
|
* @private
|
|
|
|
*/
|
2017-05-03 02:29:33 +03:00
|
|
|
async _onPasswordChanged() {
|
|
|
|
if (!(await this.fxAccounts.sessionStatus())) {
|
|
|
|
await this.fxAccounts.resetCredentials();
|
2017-04-14 22:51:39 +03:00
|
|
|
Services.obs.notifyObservers(null, ON_ACCOUNT_STATE_CHANGE_NOTIFICATION);
|
2016-07-30 06:51:16 +03:00
|
|
|
}
|
2017-05-03 02:29:33 +03:00
|
|
|
},
|
2016-03-07 08:00:34 +03:00
|
|
|
/**
|
|
|
|
* Fired when the Push server drops a subscription, or the subscription identifier changes.
|
|
|
|
*
|
|
|
|
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPushService#Receiving_Push_Messages
|
|
|
|
*
|
2016-07-30 06:51:16 +03:00
|
|
|
* @returns {Promise}
|
2016-03-07 08:00:34 +03:00
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
_onPushSubscriptionChange() {
|
|
|
|
this.log.trace("FxAccountsPushService _onPushSubscriptionChange");
|
2016-07-30 06:51:16 +03:00
|
|
|
return this.fxAccounts.updateDeviceRegistration();
|
2016-03-07 08:00:34 +03:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Unsubscribe from the Push server
|
|
|
|
*
|
|
|
|
* Ref: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIPushService#unsubscribe()
|
|
|
|
*
|
|
|
|
* @returns {Promise}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
unsubscribe() {
|
|
|
|
this.log.trace("FxAccountsPushService unsubscribe");
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
this.pushService.unsubscribe(FXA_PUSH_SCOPE_ACCOUNT_UPDATE,
|
|
|
|
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
|
|
(result, ok) => {
|
|
|
|
if (Components.isSuccessCode(result)) {
|
|
|
|
if (ok === true) {
|
|
|
|
this.log.debug("FxAccountsPushService unsubscribed");
|
|
|
|
} else {
|
|
|
|
this.log.debug("FxAccountsPushService had no subscription to unsubscribe");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.log.warn("FxAccountsPushService failed to unsubscribe", result);
|
|
|
|
}
|
|
|
|
return resolve(ok);
|
2016-07-30 06:51:16 +03:00
|
|
|
});
|
|
|
|
});
|
2016-03-07 08:00:34 +03:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Service registration below registers with FxAccountsComponents.manifest
|
|
|
|
const components = [FxAccountsPushService];
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|