Bug 1038648: [Loop] ensure exposed objects are immutable to prevent abuse by others. r=dolske

This commit is contained in:
Mike de Boer 2014-07-23 19:08:21 +02:00
parent 73a68a8ebb
commit 9102cf83c7
3 changed files with 52 additions and 55 deletions

View File

@ -33,7 +33,6 @@ function injectLoopAPI(targetWindow) {
*/
doNotDisturb: {
enumerable: true,
configurable: true,
get: function() {
return MozLoopService.doNotDisturb;
},
@ -49,7 +48,6 @@ function injectLoopAPI(targetWindow) {
*/
locale: {
enumerable: true,
configurable: true,
get: function() {
return MozLoopService.locale;
}
@ -65,7 +63,6 @@ function injectLoopAPI(targetWindow) {
*/
getStrings: {
enumerable: true,
configurable: true,
writable: true,
value: function(key) {
return MozLoopService.getStrings(key);
@ -85,7 +82,6 @@ function injectLoopAPI(targetWindow) {
*/
ensureRegistered: {
enumerable: true,
configurable: true,
writable: true,
value: function(callback) {
// We translate from a promise to a callback, as we can't pass promises from
@ -112,7 +108,6 @@ function injectLoopAPI(targetWindow) {
*/
noteCallUrlExpiry: {
enumerable: true,
configurable: true,
writable: true,
value: function(expiryTimeSeconds) {
MozLoopService.noteCallUrlExpiry(expiryTimeSeconds);
@ -130,7 +125,6 @@ function injectLoopAPI(targetWindow) {
*/
setLoopCharPref: {
enumerable: true,
configurable: true,
writable: true,
value: function(prefName, value) {
MozLoopService.setLoopCharPref(prefName, value);
@ -152,7 +146,6 @@ function injectLoopAPI(targetWindow) {
*/
getLoopCharPref: {
enumerable: true,
configurable: true,
writable: true,
value: function(prefName) {
return MozLoopService.getLoopCharPref(prefName);
@ -164,7 +157,6 @@ function injectLoopAPI(targetWindow) {
*/
startAlerting: {
enumerable: true,
configurable: true,
writable: true,
value: function() {
let chromeWindow = getChromeWindow(targetWindow);
@ -188,7 +180,6 @@ function injectLoopAPI(targetWindow) {
*/
stopAlerting: {
enumerable: true,
configurable: true,
writable: true,
value: function() {
if (ringerStopper) {
@ -224,7 +215,6 @@ function injectLoopAPI(targetWindow) {
*/
hawkRequest: {
enumerable: true,
configurable: true,
writable: true,
value: function(path, method, payloadObj, callback) {
// XXX Should really return a DOM promise here.
@ -239,6 +229,7 @@ function injectLoopAPI(targetWindow) {
let contentObj = Cu.createObjectIn(targetWindow);
Object.defineProperties(contentObj, api);
Object.seal(contentObj);
Cu.makeObjectPropsNormal(contentObj);
targetWindow.navigator.wrappedJSObject.__defineGetter__("mozLoop", function() {

View File

@ -47,6 +47,16 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
// The current deferred for the registration process. This is set if in progress
// or the registration was successful. This is null if a registration attempt was
// unsuccessful.
let gRegisteredDeferred = null;
let gPushHandler = null;
let gHawkClient = null;
let gRegisteredLoopServer = false;
let gLocalizedStrings = null;
let gInitializeTimer = null;
/**
* Internal helper methods and state
*
@ -56,12 +66,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
*/
let MozLoopServiceInternal = {
// The uri of the Loop server.
loopServerUri: Services.prefs.getCharPref("loop.server"),
// The current deferred for the registration process. This is set if in progress
// or the registration was successful. This is null if a registration attempt was
// unsuccessful.
_registeredDeferred: null,
get loopServerUri() Services.prefs.getCharPref("loop.server"),
/**
* The initial delay for push registration. This ensures we don't start
@ -137,18 +142,18 @@ let MozLoopServiceInternal = {
* rejected with an error code or string.
*/
promiseRegisteredWithServers: function(mockPushHandler) {
if (this._registeredDeferred) {
return this._registeredDeferred.promise;
if (gRegisteredDeferred) {
return gRegisteredDeferred.promise;
}
this._registeredDeferred = Promise.defer();
gRegisteredDeferred = Promise.defer();
// We grab the promise early in case .initialize or its results sets
// it back to null on error.
let result = this._registeredDeferred.promise;
let result = gRegisteredDeferred.promise;
this._pushHandler = mockPushHandler || MozLoopPushHandler;
gPushHandler = mockPushHandler || MozLoopPushHandler;
this._pushHandler.initialize(this.onPushRegistered.bind(this),
gPushHandler.initialize(this.onPushRegistered.bind(this),
this.onHandleNotification.bind(this));
return result;
@ -168,8 +173,8 @@ let MozLoopServiceInternal = {
* rejected with this JSON-parsed response.
*/
hawkRequest: function(path, method, payloadObj) {
if (!this._hawkClient) {
this._hawkClient = new HawkClient(this.loopServerUri);
if (!gHawkClient) {
gHawkClient = new HawkClient(this.loopServerUri);
}
let sessionToken;
@ -186,7 +191,7 @@ let MozLoopServiceInternal = {
2 * 32, true);
}
return this._hawkClient.request(path, method, credentials, payloadObj);
return gHawkClient.request(path, method, credentials, payloadObj);
},
/**
@ -205,8 +210,8 @@ let MozLoopServiceInternal = {
} else {
// XXX Bubble the precise details up to the UI somehow (bug 1013248).
console.warn("Loop server sent an invalid session token");
this._registeredDeferred.reject("session-token-wrong-size");
this._registeredDeferred = null;
gRegisteredDeferred.reject("session-token-wrong-size");
gRegisteredDeferred = null;
return false;
}
}
@ -221,8 +226,8 @@ let MozLoopServiceInternal = {
*/
onPushRegistered: function(err, pushUrl) {
if (err) {
this._registeredDeferred.reject(err);
this._registeredDeferred = null;
gRegisteredDeferred.reject(err);
gRegisteredDeferred = null;
return;
}
@ -239,13 +244,12 @@ let MozLoopServiceInternal = {
this.hawkRequest("/registration", "POST", { simple_push_url: pushUrl})
.then((response) => {
// If this failed we got an invalid token. storeSessionToken rejects
// the _registeredDeferred promise for us, so here we just need to
// the gRegisteredDeferred promise for us, so here we just need to
// early return.
if (!this.storeSessionToken(response.headers))
return;
this.registeredLoopServer = true;
this._registeredDeferred.resolve();
gRegisteredDeferred.resolve();
// No need to clear the promise here, everything was good, so we don't need
// to re-register.
}, (error) => {
@ -266,8 +270,8 @@ let MozLoopServiceInternal = {
// XXX Bubble the precise details up to the UI somehow (bug 1013248).
Cu.reportError("Failed to register with the loop server. error: " + error);
this._registeredDeferred.reject(error.errno);
this._registeredDeferred = null;
gRegisteredDeferred.reject(error.errno);
gRegisteredDeferred = null;
}
);
},
@ -293,8 +297,8 @@ let MozLoopServiceInternal = {
* @returns {Object} a map of element ids with attributes to set.
*/
get localizedStrings() {
if (this._localizedStrings)
return this._localizedStrings;
if (gLocalizedStrings)
return gLocalizedStrings;
var stringBundle =
Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
@ -316,7 +320,7 @@ let MozLoopServiceInternal = {
map[key][property] = string.value;
}
return this._localizedStrings = map;
return gLocalizedStrings = map;
},
/**
@ -445,11 +449,28 @@ let MozLoopServiceInternal = {
Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);
}
};
Object.freeze(MozLoopServiceInternal);
let gInitializeTimerFunc = () => {
// Kick off the push notification service into registering after a timeout
// this ensures we're not doing too much straight after the browser's finished
// starting up.
gInitializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
gInitializeTimer.initWithCallback(() => {
MozLoopService.register();
gInitializeTimer = null;
},
MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
};
/**
* Public API
*/
this.MozLoopService = {
set initializeTimerFunc(value) {
gInitializeTimerFunc = value;
},
/**
* Initialized the loop service, and starts registration with the
* push and loop servers.
@ -462,26 +483,10 @@ this.MozLoopService = {
// If expiresTime is in the future then kick-off registration.
if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
this._startInitializeTimer();
gInitializeTimerFunc();
}
},
/**
* Internal function, exposed for testing purposes only. Used to start the
* initialize timer.
*/
_startInitializeTimer: function() {
// Kick off the push notification service into registering after a timeout
// this ensures we're not doing too much straight after the browser's finished
// starting up.
this._initializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._initializeTimer.initWithCallback(function() {
this.register();
this._initializeTimer = null;
}.bind(this),
MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
},
/**
* Starts registration of Loop with the push server, and then will register
* with the Loop server. It will return early if already registered.
@ -623,3 +628,4 @@ this.MozLoopService = {
return MozLoopServiceInternal.hawkRequest(path, method, payloadObj);
},
};
Object.freeze(this.MozLoopService);

View File

@ -52,7 +52,7 @@ function run_test()
{
// Override MozLoopService's initializeTimer, so that we can verify the timeout is called
// correctly.
MozLoopService._startInitializeTimer = function() {
MozLoopService.initializeTimerFunc = function() {
startTimerCalled = true;
};