michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const DEBUG = false; michael@0: function debug(s) { dump("-*- Fallback ContactService component: " + s + "\n"); } michael@0: michael@0: const Cu = Components.utils; michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["ContactService"]; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/ContactDB.jsm"); michael@0: Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "ppmm", michael@0: "@mozilla.org/parentprocessmessagemanager;1", michael@0: "nsIMessageListenerManager"); michael@0: michael@0: michael@0: /* all exported symbols need to be bound to this on B2G - Bug 961777 */ michael@0: let ContactService = this.ContactService = { michael@0: init: function() { michael@0: if (DEBUG) debug("Init"); michael@0: this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:SendNow", michael@0: "Contacts:Clear", "Contact:Save", michael@0: "Contact:Remove", "Contacts:RegisterForMessages", michael@0: "child-process-shutdown", "Contacts:GetRevision", michael@0: "Contacts:GetCount"]; michael@0: this._children = []; michael@0: this._cursors = new Map(); michael@0: this._messages.forEach(function(msgName) { michael@0: ppmm.addMessageListener(msgName, this); michael@0: }.bind(this)); michael@0: michael@0: this._db = new ContactDB(); michael@0: this._db.init(); michael@0: michael@0: this.configureSubstringMatching(); michael@0: michael@0: Services.obs.addObserver(this, "profile-before-change", false); michael@0: Services.prefs.addObserver("ril.lastKnownSimMcc", this, false); michael@0: }, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: if (aTopic === 'profile-before-change') { michael@0: this._messages.forEach(function(msgName) { michael@0: ppmm.removeMessageListener(msgName, this); michael@0: }.bind(this)); michael@0: Services.obs.removeObserver(this, "profile-before-change"); michael@0: Services.prefs.removeObserver("dom.phonenumber.substringmatching", this); michael@0: ppmm = null; michael@0: this._messages = null; michael@0: if (this._db) michael@0: this._db.close(); michael@0: this._db = null; michael@0: this._children = null; michael@0: this._cursors = null; michael@0: } else if (aTopic === 'nsPref:changed' && aData === "ril.lastKnownSimMcc") { michael@0: this.configureSubstringMatching(); michael@0: } michael@0: }, michael@0: michael@0: configureSubstringMatching: function() { michael@0: let countryName = PhoneNumberUtils.getCountryName(); michael@0: if (Services.prefs.getPrefType("dom.phonenumber.substringmatching." + countryName) == Ci.nsIPrefBranch.PREF_INT) { michael@0: let val = Services.prefs.getIntPref("dom.phonenumber.substringmatching." + countryName); michael@0: if (val) { michael@0: this._db.enableSubstringMatching(val); michael@0: return; michael@0: } michael@0: } michael@0: // if we got here, we dont have a substring setting michael@0: // for this country, so disable substring matching michael@0: this._db.disableSubstringMatching(); michael@0: }, michael@0: michael@0: assertPermission: function(aMessage, aPerm) { michael@0: if (!aMessage.target.assertPermission(aPerm)) { michael@0: Cu.reportError("Contacts message " + aMessage.name + michael@0: " from a content process with no" + aPerm + " privileges."); michael@0: return false; michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: broadcastMessage: function broadcastMessage(aMsgName, aContent) { michael@0: this._children.forEach(function(msgMgr) { michael@0: msgMgr.sendAsyncMessage(aMsgName, aContent); michael@0: }); michael@0: }, michael@0: michael@0: receiveMessage: function(aMessage) { michael@0: if (DEBUG) debug("receiveMessage " + aMessage.name); michael@0: let mm = aMessage.target; michael@0: let msg = aMessage.data; michael@0: michael@0: switch (aMessage.name) { michael@0: case "Contacts:Find": michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return null; michael@0: } michael@0: let result = []; michael@0: this._db.find( michael@0: function(contacts) { michael@0: for (let i in contacts) { michael@0: result.push(contacts[i]); michael@0: } michael@0: michael@0: if (DEBUG) debug("result:" + JSON.stringify(result)); michael@0: mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result}); michael@0: }.bind(this), michael@0: function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this), michael@0: msg.options.findOptions); michael@0: break; michael@0: case "Contacts:GetAll": michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return null; michael@0: } michael@0: let cursorList = this._cursors.get(mm); michael@0: if (!cursorList) { michael@0: cursorList = []; michael@0: this._cursors.set(mm, cursorList); michael@0: } michael@0: cursorList.push(msg.cursorId); michael@0: michael@0: this._db.getAll( michael@0: function(aContacts) { michael@0: try { michael@0: mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts}); michael@0: if (aContacts === null) { michael@0: let cursorList = this._cursors.get(mm); michael@0: let index = cursorList.indexOf(msg.cursorId); michael@0: cursorList.splice(index, 1); michael@0: } michael@0: } catch (e) { michael@0: if (DEBUG) debug("Child is dead, DB should stop sending contacts"); michael@0: throw e; michael@0: } michael@0: }.bind(this), michael@0: function(aErrorMsg) { mm.sendAsyncMessage("Contacts:GetAll:Return:KO", { requestID: msg.cursorId, errorMsg: aErrorMsg }); }, michael@0: msg.findOptions, msg.cursorId); michael@0: break; michael@0: case "Contacts:GetAll:SendNow": michael@0: // sendNow is a no op if there isn't an existing cursor in the DB, so we michael@0: // don't need to assert the permission again. michael@0: this._db.sendNow(msg.cursorId); michael@0: break; michael@0: case "Contact:Save": michael@0: if (msg.options.reason === "create") { michael@0: if (!this.assertPermission(aMessage, "contacts-create")) { michael@0: return null; michael@0: } michael@0: } else { michael@0: if (!this.assertPermission(aMessage, "contacts-write")) { michael@0: return null; michael@0: } michael@0: } michael@0: this._db.saveContact( michael@0: msg.options.contact, michael@0: function() { michael@0: mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.options.contact.id }); michael@0: this.broadcastMessage("Contact:Changed", { contactID: msg.options.contact.id, reason: msg.options.reason }); michael@0: }.bind(this), michael@0: function(aErrorMsg) { mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) michael@0: ); michael@0: break; michael@0: case "Contact:Remove": michael@0: if (!this.assertPermission(aMessage, "contacts-write")) { michael@0: return null; michael@0: } michael@0: this._db.removeContact( michael@0: msg.options.id, michael@0: function() { michael@0: mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.options.id }); michael@0: this.broadcastMessage("Contact:Changed", { contactID: msg.options.id, reason: "remove" }); michael@0: }.bind(this), michael@0: function(aErrorMsg) { mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) michael@0: ); michael@0: break; michael@0: case "Contacts:Clear": michael@0: if (!this.assertPermission(aMessage, "contacts-write")) { michael@0: return null; michael@0: } michael@0: this._db.clear( michael@0: function() { michael@0: mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); michael@0: this.broadcastMessage("Contact:Changed", { reason: "remove" }); michael@0: }.bind(this), michael@0: function(aErrorMsg) { michael@0: mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); michael@0: }.bind(this) michael@0: ); michael@0: break; michael@0: case "Contacts:GetRevision": michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return null; michael@0: } michael@0: this._db.getRevision( michael@0: function(revision) { michael@0: mm.sendAsyncMessage("Contacts:Revision", { michael@0: requestID: msg.requestID, michael@0: revision: revision michael@0: }); michael@0: }, michael@0: function(aErrorMsg) { michael@0: mm.sendAsyncMessage("Contacts:GetRevision:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); michael@0: }.bind(this) michael@0: ); michael@0: break; michael@0: case "Contacts:GetCount": michael@0: if (!this.assertPermission(aMessage, "contacts-read")) { michael@0: return null; michael@0: } michael@0: this._db.getCount( michael@0: function(count) { michael@0: mm.sendAsyncMessage("Contacts:Count", { michael@0: requestID: msg.requestID, michael@0: count: count michael@0: }); michael@0: }, michael@0: function(aErrorMsg) { michael@0: mm.sendAsyncMessage("Contacts:Count:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); michael@0: }.bind(this) michael@0: ); michael@0: break; michael@0: case "Contacts:RegisterForMessages": michael@0: if (!aMessage.target.assertPermission("contacts-read")) { michael@0: return null; michael@0: } michael@0: if (DEBUG) debug("Register!"); michael@0: if (this._children.indexOf(mm) == -1) { michael@0: this._children.push(mm); michael@0: } michael@0: break; michael@0: case "child-process-shutdown": michael@0: if (DEBUG) debug("Unregister"); michael@0: let index = this._children.indexOf(mm); michael@0: if (index != -1) { michael@0: if (DEBUG) debug("Unregister index: " + index); michael@0: this._children.splice(index, 1); michael@0: } michael@0: cursorList = this._cursors.get(mm); michael@0: if (cursorList) { michael@0: for (let id of cursorList) { michael@0: this._db.clearDispatcher(id); michael@0: } michael@0: this._cursors.delete(mm); michael@0: } michael@0: break; michael@0: default: michael@0: if (DEBUG) debug("WRONG MESSAGE NAME: " + aMessage.name); michael@0: } michael@0: } michael@0: } michael@0: michael@0: ContactService.init();