1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/contacts/fallback/ContactService.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,262 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +const DEBUG = false; 1.11 +function debug(s) { dump("-*- Fallback ContactService component: " + s + "\n"); } 1.12 + 1.13 +const Cu = Components.utils; 1.14 +const Cc = Components.classes; 1.15 +const Ci = Components.interfaces; 1.16 + 1.17 +this.EXPORTED_SYMBOLS = ["ContactService"]; 1.18 + 1.19 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.20 +Cu.import("resource://gre/modules/Services.jsm"); 1.21 +Cu.import("resource://gre/modules/ContactDB.jsm"); 1.22 +Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); 1.23 + 1.24 +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", 1.25 + "@mozilla.org/parentprocessmessagemanager;1", 1.26 + "nsIMessageListenerManager"); 1.27 + 1.28 + 1.29 +/* all exported symbols need to be bound to this on B2G - Bug 961777 */ 1.30 +let ContactService = this.ContactService = { 1.31 + init: function() { 1.32 + if (DEBUG) debug("Init"); 1.33 + this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:SendNow", 1.34 + "Contacts:Clear", "Contact:Save", 1.35 + "Contact:Remove", "Contacts:RegisterForMessages", 1.36 + "child-process-shutdown", "Contacts:GetRevision", 1.37 + "Contacts:GetCount"]; 1.38 + this._children = []; 1.39 + this._cursors = new Map(); 1.40 + this._messages.forEach(function(msgName) { 1.41 + ppmm.addMessageListener(msgName, this); 1.42 + }.bind(this)); 1.43 + 1.44 + this._db = new ContactDB(); 1.45 + this._db.init(); 1.46 + 1.47 + this.configureSubstringMatching(); 1.48 + 1.49 + Services.obs.addObserver(this, "profile-before-change", false); 1.50 + Services.prefs.addObserver("ril.lastKnownSimMcc", this, false); 1.51 + }, 1.52 + 1.53 + observe: function(aSubject, aTopic, aData) { 1.54 + if (aTopic === 'profile-before-change') { 1.55 + this._messages.forEach(function(msgName) { 1.56 + ppmm.removeMessageListener(msgName, this); 1.57 + }.bind(this)); 1.58 + Services.obs.removeObserver(this, "profile-before-change"); 1.59 + Services.prefs.removeObserver("dom.phonenumber.substringmatching", this); 1.60 + ppmm = null; 1.61 + this._messages = null; 1.62 + if (this._db) 1.63 + this._db.close(); 1.64 + this._db = null; 1.65 + this._children = null; 1.66 + this._cursors = null; 1.67 + } else if (aTopic === 'nsPref:changed' && aData === "ril.lastKnownSimMcc") { 1.68 + this.configureSubstringMatching(); 1.69 + } 1.70 + }, 1.71 + 1.72 + configureSubstringMatching: function() { 1.73 + let countryName = PhoneNumberUtils.getCountryName(); 1.74 + if (Services.prefs.getPrefType("dom.phonenumber.substringmatching." + countryName) == Ci.nsIPrefBranch.PREF_INT) { 1.75 + let val = Services.prefs.getIntPref("dom.phonenumber.substringmatching." + countryName); 1.76 + if (val) { 1.77 + this._db.enableSubstringMatching(val); 1.78 + return; 1.79 + } 1.80 + } 1.81 + // if we got here, we dont have a substring setting 1.82 + // for this country, so disable substring matching 1.83 + this._db.disableSubstringMatching(); 1.84 + }, 1.85 + 1.86 + assertPermission: function(aMessage, aPerm) { 1.87 + if (!aMessage.target.assertPermission(aPerm)) { 1.88 + Cu.reportError("Contacts message " + aMessage.name + 1.89 + " from a content process with no" + aPerm + " privileges."); 1.90 + return false; 1.91 + } 1.92 + return true; 1.93 + }, 1.94 + 1.95 + broadcastMessage: function broadcastMessage(aMsgName, aContent) { 1.96 + this._children.forEach(function(msgMgr) { 1.97 + msgMgr.sendAsyncMessage(aMsgName, aContent); 1.98 + }); 1.99 + }, 1.100 + 1.101 + receiveMessage: function(aMessage) { 1.102 + if (DEBUG) debug("receiveMessage " + aMessage.name); 1.103 + let mm = aMessage.target; 1.104 + let msg = aMessage.data; 1.105 + 1.106 + switch (aMessage.name) { 1.107 + case "Contacts:Find": 1.108 + if (!this.assertPermission(aMessage, "contacts-read")) { 1.109 + return null; 1.110 + } 1.111 + let result = []; 1.112 + this._db.find( 1.113 + function(contacts) { 1.114 + for (let i in contacts) { 1.115 + result.push(contacts[i]); 1.116 + } 1.117 + 1.118 + if (DEBUG) debug("result:" + JSON.stringify(result)); 1.119 + mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result}); 1.120 + }.bind(this), 1.121 + function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this), 1.122 + msg.options.findOptions); 1.123 + break; 1.124 + case "Contacts:GetAll": 1.125 + if (!this.assertPermission(aMessage, "contacts-read")) { 1.126 + return null; 1.127 + } 1.128 + let cursorList = this._cursors.get(mm); 1.129 + if (!cursorList) { 1.130 + cursorList = []; 1.131 + this._cursors.set(mm, cursorList); 1.132 + } 1.133 + cursorList.push(msg.cursorId); 1.134 + 1.135 + this._db.getAll( 1.136 + function(aContacts) { 1.137 + try { 1.138 + mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts}); 1.139 + if (aContacts === null) { 1.140 + let cursorList = this._cursors.get(mm); 1.141 + let index = cursorList.indexOf(msg.cursorId); 1.142 + cursorList.splice(index, 1); 1.143 + } 1.144 + } catch (e) { 1.145 + if (DEBUG) debug("Child is dead, DB should stop sending contacts"); 1.146 + throw e; 1.147 + } 1.148 + }.bind(this), 1.149 + function(aErrorMsg) { mm.sendAsyncMessage("Contacts:GetAll:Return:KO", { requestID: msg.cursorId, errorMsg: aErrorMsg }); }, 1.150 + msg.findOptions, msg.cursorId); 1.151 + break; 1.152 + case "Contacts:GetAll:SendNow": 1.153 + // sendNow is a no op if there isn't an existing cursor in the DB, so we 1.154 + // don't need to assert the permission again. 1.155 + this._db.sendNow(msg.cursorId); 1.156 + break; 1.157 + case "Contact:Save": 1.158 + if (msg.options.reason === "create") { 1.159 + if (!this.assertPermission(aMessage, "contacts-create")) { 1.160 + return null; 1.161 + } 1.162 + } else { 1.163 + if (!this.assertPermission(aMessage, "contacts-write")) { 1.164 + return null; 1.165 + } 1.166 + } 1.167 + this._db.saveContact( 1.168 + msg.options.contact, 1.169 + function() { 1.170 + mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.options.contact.id }); 1.171 + this.broadcastMessage("Contact:Changed", { contactID: msg.options.contact.id, reason: msg.options.reason }); 1.172 + }.bind(this), 1.173 + function(aErrorMsg) { mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) 1.174 + ); 1.175 + break; 1.176 + case "Contact:Remove": 1.177 + if (!this.assertPermission(aMessage, "contacts-write")) { 1.178 + return null; 1.179 + } 1.180 + this._db.removeContact( 1.181 + msg.options.id, 1.182 + function() { 1.183 + mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.options.id }); 1.184 + this.broadcastMessage("Contact:Changed", { contactID: msg.options.id, reason: "remove" }); 1.185 + }.bind(this), 1.186 + function(aErrorMsg) { mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this) 1.187 + ); 1.188 + break; 1.189 + case "Contacts:Clear": 1.190 + if (!this.assertPermission(aMessage, "contacts-write")) { 1.191 + return null; 1.192 + } 1.193 + this._db.clear( 1.194 + function() { 1.195 + mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); 1.196 + this.broadcastMessage("Contact:Changed", { reason: "remove" }); 1.197 + }.bind(this), 1.198 + function(aErrorMsg) { 1.199 + mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); 1.200 + }.bind(this) 1.201 + ); 1.202 + break; 1.203 + case "Contacts:GetRevision": 1.204 + if (!this.assertPermission(aMessage, "contacts-read")) { 1.205 + return null; 1.206 + } 1.207 + this._db.getRevision( 1.208 + function(revision) { 1.209 + mm.sendAsyncMessage("Contacts:Revision", { 1.210 + requestID: msg.requestID, 1.211 + revision: revision 1.212 + }); 1.213 + }, 1.214 + function(aErrorMsg) { 1.215 + mm.sendAsyncMessage("Contacts:GetRevision:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); 1.216 + }.bind(this) 1.217 + ); 1.218 + break; 1.219 + case "Contacts:GetCount": 1.220 + if (!this.assertPermission(aMessage, "contacts-read")) { 1.221 + return null; 1.222 + } 1.223 + this._db.getCount( 1.224 + function(count) { 1.225 + mm.sendAsyncMessage("Contacts:Count", { 1.226 + requestID: msg.requestID, 1.227 + count: count 1.228 + }); 1.229 + }, 1.230 + function(aErrorMsg) { 1.231 + mm.sendAsyncMessage("Contacts:Count:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); 1.232 + }.bind(this) 1.233 + ); 1.234 + break; 1.235 + case "Contacts:RegisterForMessages": 1.236 + if (!aMessage.target.assertPermission("contacts-read")) { 1.237 + return null; 1.238 + } 1.239 + if (DEBUG) debug("Register!"); 1.240 + if (this._children.indexOf(mm) == -1) { 1.241 + this._children.push(mm); 1.242 + } 1.243 + break; 1.244 + case "child-process-shutdown": 1.245 + if (DEBUG) debug("Unregister"); 1.246 + let index = this._children.indexOf(mm); 1.247 + if (index != -1) { 1.248 + if (DEBUG) debug("Unregister index: " + index); 1.249 + this._children.splice(index, 1); 1.250 + } 1.251 + cursorList = this._cursors.get(mm); 1.252 + if (cursorList) { 1.253 + for (let id of cursorList) { 1.254 + this._db.clearDispatcher(id); 1.255 + } 1.256 + this._cursors.delete(mm); 1.257 + } 1.258 + break; 1.259 + default: 1.260 + if (DEBUG) debug("WRONG MESSAGE NAME: " + aMessage.name); 1.261 + } 1.262 + } 1.263 +} 1.264 + 1.265 +ContactService.init();