mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-15 22:35:43 +00:00
Bug 1078309: use a different database for each Fx Account. r=abr,paolo
This commit is contained in:
parent
bb02e2d8cf
commit
f09732faab
@ -25,7 +25,9 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LoopStorage"];
|
||||
|
||||
const kDatabaseName = "loop";
|
||||
const kDatabasePrefix = "loop-";
|
||||
const kDefaultDatabaseName = "default";
|
||||
let gDatabaseName = kDatabasePrefix + kDefaultDatabaseName;
|
||||
const kDatabaseVersion = 1;
|
||||
|
||||
let gWaitForOpenCallbacks = new Set();
|
||||
@ -83,7 +85,7 @@ const ensureDatabaseOpen = function(onOpen) {
|
||||
gWaitForOpenCallbacks.clear();
|
||||
};
|
||||
|
||||
let openRequest = indexedDB.open(kDatabaseName, kDatabaseVersion);
|
||||
let openRequest = indexedDB.open(gDatabaseName, kDatabaseVersion);
|
||||
|
||||
openRequest.onblocked = function(event) {
|
||||
invokeCallbacks(new Error("Database cannot be upgraded cause in use: " + event.target.error));
|
||||
@ -92,7 +94,7 @@ const ensureDatabaseOpen = function(onOpen) {
|
||||
openRequest.onerror = function(event) {
|
||||
// Try to delete the old database so that we can start this process over
|
||||
// next time.
|
||||
indexedDB.deleteDatabase(kDatabaseName);
|
||||
indexedDB.deleteDatabase(gDatabaseName);
|
||||
invokeCallbacks(new Error("Error while opening database: " + event.target.errorCode));
|
||||
};
|
||||
|
||||
@ -109,6 +111,33 @@ const ensureDatabaseOpen = function(onOpen) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch to a database with a different name by closing the current connection
|
||||
* and making sure that the next connection attempt will be made using the updated
|
||||
* name.
|
||||
*
|
||||
* @param {String} name New name of the database to switch to.
|
||||
*/
|
||||
const switchDatabase = function(name) {
|
||||
if (!name) {
|
||||
name = kDefaultDatabaseName;
|
||||
}
|
||||
name = kDatabasePrefix + name;
|
||||
if (name == gDatabaseName) {
|
||||
// This is already the current database, so there's no need to switch.
|
||||
return;
|
||||
}
|
||||
|
||||
gDatabaseName = name;
|
||||
if (gDatabase) {
|
||||
try {
|
||||
gDatabase.close();
|
||||
} finally {
|
||||
gDatabase = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a transaction on the loop database and return it.
|
||||
*
|
||||
@ -179,6 +208,14 @@ const getStore = function(store, callback, mode) {
|
||||
* database is loaded in memory and consumers will be able to change its structure.
|
||||
*/
|
||||
this.LoopStorage = Object.freeze({
|
||||
/**
|
||||
* @var {String} databaseName The name of the database that is currently active,
|
||||
* WITHOUT the prefix
|
||||
*/
|
||||
get databaseName() {
|
||||
return gDatabaseName.substr(kDatabasePrefix.length);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a connection to the IndexedDB database and return the database object.
|
||||
*
|
||||
@ -191,6 +228,16 @@ this.LoopStorage = Object.freeze({
|
||||
ensureDatabaseOpen(callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch to a database with a different name.
|
||||
*
|
||||
* @param {String} name New name of the database to switch to. Defaults to
|
||||
* `kDefaultDatabaseName`
|
||||
*/
|
||||
switchDatabase: function(name = kDefaultDatabaseName) {
|
||||
switchDatabase(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start a transaction on the loop database and return it.
|
||||
* If only two arguments are passed, the default mode will be assumed and the
|
||||
|
@ -56,6 +56,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "HawkClient",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
|
||||
"resource://services-common/hawkrequest.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
|
||||
"resource:///modules/loop/LoopStorage.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
||||
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
||||
|
||||
@ -367,6 +370,8 @@ let MozLoopServiceInternal = {
|
||||
|
||||
notifyStatusChanged: function(aReason = null) {
|
||||
log.debug("notifyStatusChanged with reason:", aReason);
|
||||
let profile = MozLoopService.userProfile;
|
||||
LoopStorage.switchDatabase(profile ? profile.uid : null);
|
||||
Services.obs.notifyObservers(null, "loop-status-changed", aReason);
|
||||
},
|
||||
|
||||
|
@ -153,14 +153,14 @@ loop.contacts = (function(_, mozL10n) {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
componentShouldUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
let currContact = this.props.contact;
|
||||
let nextContact = nextProps.contact;
|
||||
return (
|
||||
currContact.name[0] !== nextContact.name[0] ||
|
||||
currContact.blocked !== nextContact.blocked ||
|
||||
getPreferredEmail(currContact).value !==
|
||||
getPreferredEmail(nextContact).value
|
||||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
|
||||
nextState.showMenu !== this.state.showMenu
|
||||
);
|
||||
},
|
||||
|
||||
@ -215,20 +215,32 @@ loop.contacts = (function(_, mozL10n) {
|
||||
const ContactsList = React.createClass({displayName: 'ContactsList',
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
/**
|
||||
* Contacts collection object
|
||||
*/
|
||||
contacts: null,
|
||||
|
||||
/**
|
||||
* User profile
|
||||
*/
|
||||
_userProfile: null,
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {},
|
||||
importBusy: false,
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
refresh: function(callback = function() {}) {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
this.handleContactRemoveAll();
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
@ -239,11 +251,32 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// Take the time to initialize class variables that are used outside
|
||||
// `this.state`.
|
||||
this.contacts = {};
|
||||
this._userProfile = navigator.mozLoop.userProfile;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
|
||||
this.refresh(err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
@ -261,8 +294,24 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
let profile = navigator.mozLoop.userProfile;
|
||||
let currUid = this._userProfile ? this._userProfile.uid : null;
|
||||
let newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), reload all contacts.
|
||||
this._userProfile = profile;
|
||||
// The following will do a forceUpdate() for us.
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact, render = true) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
if (render) {
|
||||
@ -271,7 +320,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
@ -281,7 +330,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
// Do not allow any race conditions when removing all contacts.
|
||||
this.contacts = {};
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
handleImportButtonClick: function() {
|
||||
@ -364,11 +415,11 @@ loop.contacts = (function(_, mozL10n) {
|
||||
handleContactAction: this.handleContactAction})
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
let shownContacts = _.groupBy(this.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
|
||||
let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
|
||||
MIN_CONTACTS_FOR_FILTERING;
|
||||
if (showFilter) {
|
||||
let filter = this.state.filter.trim().toLocaleLowerCase();
|
||||
|
@ -153,14 +153,14 @@ loop.contacts = (function(_, mozL10n) {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
componentShouldUpdate: function(nextProps, nextState) {
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
let currContact = this.props.contact;
|
||||
let nextContact = nextProps.contact;
|
||||
return (
|
||||
currContact.name[0] !== nextContact.name[0] ||
|
||||
currContact.blocked !== nextContact.blocked ||
|
||||
getPreferredEmail(currContact).value !==
|
||||
getPreferredEmail(nextContact).value
|
||||
getPreferredEmail(currContact).value !== getPreferredEmail(nextContact).value ||
|
||||
nextState.showMenu !== this.state.showMenu
|
||||
);
|
||||
},
|
||||
|
||||
@ -215,20 +215,32 @@ loop.contacts = (function(_, mozL10n) {
|
||||
const ContactsList = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
/**
|
||||
* Contacts collection object
|
||||
*/
|
||||
contacts: null,
|
||||
|
||||
/**
|
||||
* User profile
|
||||
*/
|
||||
_userProfile: null,
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
contacts: {},
|
||||
importBusy: false,
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
refresh: function(callback = function() {}) {
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
this.handleContactRemoveAll();
|
||||
|
||||
contactsAPI.getAll((err, contacts) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add contacts already present in the DB. We do this in timed chunks to
|
||||
@ -239,11 +251,32 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
if (contacts.length) {
|
||||
setTimeout(addContactsInChunks, 0);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
addContactsInChunks(contacts);
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// Take the time to initialize class variables that are used outside
|
||||
// `this.state`.
|
||||
this.contacts = {};
|
||||
this._userProfile = navigator.mozLoop.userProfile;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
|
||||
this.refresh(err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
let contactsAPI = navigator.mozLoop.contacts;
|
||||
|
||||
// Listen for contact changes/ updates.
|
||||
contactsAPI.on("add", (eventName, contact) => {
|
||||
@ -261,8 +294,24 @@ loop.contacts = (function(_, mozL10n) {
|
||||
});
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
let profile = navigator.mozLoop.userProfile;
|
||||
let currUid = this._userProfile ? this._userProfile.uid : null;
|
||||
let newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), reload all contacts.
|
||||
this._userProfile = profile;
|
||||
// The following will do a forceUpdate() for us.
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
handleContactAddOrUpdate: function(contact, render = true) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
contacts[guid] = contact;
|
||||
if (render) {
|
||||
@ -271,7 +320,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
},
|
||||
|
||||
handleContactRemove: function(contact) {
|
||||
let contacts = this.state.contacts;
|
||||
let contacts = this.contacts;
|
||||
let guid = String(contact._guid);
|
||||
if (!contacts[guid]) {
|
||||
return;
|
||||
@ -281,7 +330,9 @@ loop.contacts = (function(_, mozL10n) {
|
||||
},
|
||||
|
||||
handleContactRemoveAll: function() {
|
||||
this.setState({contacts: {}});
|
||||
// Do not allow any race conditions when removing all contacts.
|
||||
this.contacts = {};
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
handleImportButtonClick: function() {
|
||||
@ -364,11 +415,11 @@ loop.contacts = (function(_, mozL10n) {
|
||||
handleContactAction={this.handleContactAction} />
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
let shownContacts = _.groupBy(this.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
let showFilter = Object.getOwnPropertyNames(this.state.contacts).length >=
|
||||
let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
|
||||
MIN_CONTACTS_FOR_FILTERING;
|
||||
if (showFilter) {
|
||||
let filter = this.state.filter.trim().toLocaleLowerCase();
|
||||
|
@ -625,7 +625,9 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
_onStatusChanged: function() {
|
||||
var profile = navigator.mozLoop.userProfile;
|
||||
if (profile != this.state.userProfile) {
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab("call");
|
||||
}
|
||||
|
@ -625,7 +625,9 @@ loop.panel = (function(_, mozL10n) {
|
||||
|
||||
_onStatusChanged: function() {
|
||||
var profile = navigator.mozLoop.userProfile;
|
||||
if (profile != this.state.userProfile) {
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab("call");
|
||||
}
|
||||
|
@ -2,6 +2,11 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {LoopContacts} = Cu.import("resource:///modules/loop/LoopContacts.jsm", {});
|
||||
const {LoopStorage} = Cu.import("resource:///modules/loop/LoopStorage.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
const kContacts = [{
|
||||
id: 1,
|
||||
@ -400,3 +405,31 @@ add_task(function* () {
|
||||
Assert.strictEqual(gExpectedRemovals.length, 0, "No contact removals should be expected anymore");
|
||||
Assert.strictEqual(gExpectedUpdates.length, 0, "No contact updates should be expected anymore");
|
||||
});
|
||||
|
||||
// Test switching between different databases.
|
||||
add_task(function* () {
|
||||
Assert.equal(LoopStorage.databaseName, "default", "First active partition should be the default");
|
||||
yield promiseLoadContacts();
|
||||
|
||||
let uuid = uuidgen.generateUUID().toString().replace(/[{}]+/g, "");
|
||||
LoopStorage.switchDatabase(uuid);
|
||||
Assert.equal(LoopStorage.databaseName, uuid, "The active partition should have changed");
|
||||
|
||||
yield promiseLoadContacts();
|
||||
|
||||
let contacts = yield promiseLoadContacts();
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
|
||||
LoopStorage.switchDatabase();
|
||||
Assert.equal(LoopStorage.databaseName, "default", "The active partition should have changed");
|
||||
|
||||
LoopContacts.getAll(function(err, contacts) {
|
||||
Assert.equal(err, null, "There shouldn't be an error");
|
||||
|
||||
for (let i = 0, l = contacts.length; i < l; ++i) {
|
||||
compareContacts(contacts[i], kContacts[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user