Bug 890909 - [Contacts API] migration code problem when matching using the substringMatching pref (eg in Brazil). r=gwagner, a=leo+
authorMichael Henretty <mhenretty@mozilla.com>
Mon, 22 Jul 2013 10:00:22 -0700
changeset 119815 8135299f3efd733604082954cc7191b11243639e
parent 119814 e8ded8d2e5cf277515661963b55f437d9feb6100
child 119816 c4c6d2fe8d52bf8dee3d79c5487f3fe1c837306e
push id1007
push usergwagner@mozilla.com
push dateMon, 29 Jul 2013 02:19:00 +0000
reviewersgwagner, leo
bugs890909
milestone18.1
Bug 890909 - [Contacts API] migration code problem when matching using the substringMatching pref (eg in Brazil). r=gwagner, a=leo+
b2g/app/b2g.js
dom/contacts/fallback/ContactDB.jsm
dom/contacts/fallback/ContactService.jsm
dom/contacts/tests/test_contacts_substringmatching.html
dom/network/interfaces/nsIDOMMobileConnection.idl
dom/phonenumberutils/PhoneNumberUtils.jsm
dom/system/gonk/RILContentHelper.js
dom/system/gonk/RadioInterfaceLayer.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -427,17 +427,16 @@ pref("services.push.requestTimeout", 100
 // enable udp wakeup support
 pref("services.push.udp.wakeupEnabled", true);
 // port on which UDP server socket is bound
 pref("services.push.udp.port", 2442);
 
 // NetworkStats
 #ifdef MOZ_B2G_RIL
 pref("dom.mozNetworkStats.enabled", true);
-pref("ril.lastKnownMcc", "724");
 pref("ril.cellbroadcast.disabled", false);
 #endif
 
 // WebSettings
 pref("dom.mozSettings.enabled", true);
 pref("dom.mozPermissionSettings.enabled", true);
 
 // controls if we want camera support
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -13,17 +13,17 @@ const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 
 const DB_NAME = "contacts";
-const DB_VERSION = 12;
+const DB_VERSION = 13;
 const STORE_NAME = "contacts";
 const SAVED_GETALL_STORE_NAME = "getallcache";
 const CHUNK_SIZE = 20;
 const CHUNK_INTERVAL = 500;
 const REVISION_STORE = "revision";
 const REVISION_KEY = "revision";
 
 function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearDispatcher) {
@@ -438,33 +438,69 @@ ContactDB.prototype = {
               );
               cursor.update(cursor.value);
             }
             cursor.continue();
           } else {
             next();
           }
         };
+      },
+      function upgrade12to13() {
+        if (DEBUG) debug("Add phone substring to the search index if appropriate for country");
+        if (this.substringMatching) {
+          if (!objectStore) {
+            objectStore = aTransaction.objectStore(STORE_NAME);
+          }
+          objectStore.openCursor().onsuccess = function(event) {
+            let cursor = event.target.result;
+            if (cursor) {
+              if (cursor.value.properties.tel) {
+                cursor.value.search.parsedTel = cursor.value.search.parsedTel || [];
+                cursor.value.properties.tel.forEach(
+                  function(tel) {
+                    let normalized = PhoneNumberUtils.normalize(tel.value.toString());
+                    if (normalized) {
+                      if (this.substringMatching && normalized.length > this.substringMatching) {
+                        let sub = normalized.slice(-this.substringMatching);
+                        if (cursor.value.search.parsedTel.indexOf(sub) === -1) {
+                          if (DEBUG) debug("Adding substring index: " + tel + ", " + sub);
+                          cursor.value.search.parsedTel.push(sub);
+                        }
+                      }
+                    }
+                  }.bind(this)
+                );
+                cursor.update(cursor.value);
+              }
+              cursor.continue();
+            } else {
+              next();
+            }
+          }.bind(this);
+        } else {
+          next();
+        }
       }
     ];
 
     let index = aOldVersion;
     let outer = this;
     function next() {
       if (index == aNewVersion) {
         if (aOldVersion === 0) {
           loadInitialContacts();
         }
         outer.incrementRevision(aTransaction);
         return;
       }
       try {
         var i = index++;
         if (DEBUG) debug("Upgrade step: " + i + "\n");
-        steps[i]();
+        steps[i].call(outer);
       } catch(ex) {
         dump("Caught exception" + ex);
         aTransaction.abort();
         return;
       }
     };
     if (aNewVersion > steps.length) {
       dump("Contacts DB upgrade error!");
@@ -965,15 +1001,21 @@ ContactDB.prototype = {
       for (let i in event.target.result) {
         txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]);
       }
     }.bind(this);
   },
 
   // Enable special phone number substring matching. Does not update existing DB entries.
   enableSubstringMatching: function enableSubstringMatching(aDigits) {
+    if (DEBUG) debug("MCC enabling substring matching " + aDigits);
     this.substringMatching = aDigits;
   },
 
+  disableSubstringMatching: function disableSubstringMatching() {
+    if (DEBUG) debug("MCC disabling substring matching");
+    delete this.substringMatching;
+  },
+
   init: function init(aGlobal) {
     this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME, SAVED_GETALL_STORE_NAME, REVISION_STORE], aGlobal);
   }
 };
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -52,54 +52,54 @@ this.DOMContactManager = {
       ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
     var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
     idbManager.initWindowless(myGlobal);
     this._db = new ContactDB(myGlobal);
     this._db.init(myGlobal);
 
-    let countryName = PhoneNumberUtils.getCountryName();
-    if (Services.prefs.getPrefType("dom.phonenumber.substringmatching." + countryName) == Ci.nsIPrefBranch.PREF_INT) {
-      if (DEBUG) debug("Enable Substring Matching for Phone Numbers: " + countryName);
-      let val = Services.prefs.getIntPref("dom.phonenumber.substringmatching." + countryName);
-      if (val && val > 0) {
-        this._db.enableSubstringMatching(val);
-      }
-    }
+    this.configureSubstringMatching();
 
     Services.obs.addObserver(this, "profile-before-change", false);
-    Services.prefs.addObserver("dom.phonenumber.substringmatching", this, false);
+    Services.prefs.addObserver("ril.lastKnownSimMcc", this, false);
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (aTopic === 'profile-before-change') {
       myGlobal = null;
       this._messages.forEach(function(msgName) {
         ppmm.removeMessageListener(msgName, this);
       }.bind(this));
       Services.obs.removeObserver(this, "profile-before-change");
       Services.prefs.removeObserver("dom.phonenumber.substringmatching", this);
       ppmm = null;
       this._messages = null;
       if (this._db)
         this._db.close();
       this._db = null;
-    } else if (aTopic === 'nsPref:changed' && aData.contains("dom.phonenumber.substringmatching")) {
-      // We don't fully support changing substringMatching during runtime. This is mostly for testing.
-      let countryName = PhoneNumberUtils.getCountryName();
-      if (Services.prefs.getPrefType("dom.phonenumber.substringmatching." + countryName) == Ci.nsIPrefBranch.PREF_INT) {
-        let val = Services.prefs.getIntPref("dom.phonenumber.substringmatching." + countryName);
-        if (val && val > 0) {
-          this._db.enableSubstringMatching(val);
-        }
-      }
+    } else if (aTopic === 'nsPref:changed' && aData === "ril.lastKnownSimMcc") {
+      this.configureSubstringMatching();
     }
   },
 
+  configureSubstringMatching: function() {
+    let countryName = PhoneNumberUtils.getCountryName();
+    if (Services.prefs.getPrefType("dom.phonenumber.substringmatching." + countryName) == Ci.nsIPrefBranch.PREF_INT) {
+      let val = Services.prefs.getIntPref("dom.phonenumber.substringmatching." + countryName);
+      if (val) {
+        this._db.enableSubstringMatching(val);
+        return;
+       }
+     }
+    // if we got here, we dont have a substring setting
+    // for this country, so disable substring matching
+    this._db.disableSubstringMatching();
+  },
+
   assertPermission: function(aMessage, aPerm) {
     if (!aMessage.target.assertPermission(aPerm)) {
       Cu.reportError("Contacts message " + aMessage.name +
                      " from a content process with no" + aPerm + " privileges.");
       return false;
     }
     return true;
   },
--- a/dom/contacts/tests/test_contacts_substringmatching.html
+++ b/dom/contacts/tests/test_contacts_substringmatching.html
@@ -22,16 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 "use strict";
 
 if (SpecialPowers.isMainProcess()) {
   SpecialPowers.Cu.import("resource://gre/modules/ContactService.jsm");
 }
 
 var substringLength = 8;
 SpecialPowers.setIntPref("dom.phonenumber.substringmatching.BR", substringLength);
+SpecialPowers.setCharPref("ril.lastKnownSimMcc", "724");
 
 SpecialPowers.addPermission("contacts-write", true, document);
 SpecialPowers.addPermission("contacts-read", true, document);
 SpecialPowers.addPermission("contacts-create", true, document);
 
 var sample_id1;
 var createResult1;
 var findResult1;
@@ -253,9 +254,9 @@ function next() {
   index += 1;
 }
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(next);
 </script>
 </pre>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/network/interfaces/nsIDOMMobileConnection.idl
+++ b/dom/network/interfaces/nsIDOMMobileConnection.idl
@@ -471,35 +471,30 @@ interface nsIDOMMozMobileCellInfo: nsISu
   readonly attribute unsigned short gsmLocationAreaCode;
 
   /**
    * Mobile Cell ID for GSM/WCDMA networks.
    */
   readonly attribute unsigned long gsmCellId;
 };
 
-[scriptable, uuid(f88f6820-abbe-11e2-9e96-0800200c9a66)]
+[scriptable, uuid(712d0bb8-267e-49c3-8722-e3319367d033)]
 interface nsIDOMMozMobileICCInfo : nsISupports
 {
    /**
    * Integrated Circuit Card Identifier.
    */
   readonly attribute DOMString iccid;
 
   /**
    * Mobile Country Code (MCC) of the subscriber's home network.
    */
   readonly attribute DOMString mcc;
 
   /**
-   * Mobile Country Code (MCC) of previous subscriber's home network.
-   */
-  readonly attribute DOMString lastKnownMcc;
-
-  /**
    * Mobile Network Code (MNC) of the subscriber's home network.
    */
   readonly attribute DOMString mnc;
 
   /**
    * Service Provider Name (SPN) of the subscriber's home network.
    */
   readonly attribute DOMString spn;
--- a/dom/phonenumberutils/PhoneNumberUtils.jsm
+++ b/dom/phonenumberutils/PhoneNumberUtils.jsm
@@ -11,48 +11,52 @@ function debug(s) { if(DEBUG) dump("-*- 
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/PhoneNumber.jsm");
 Cu.import("resource://gre/modules/mcc_iso3166_table.jsm");
 
 #ifdef MOZ_B2G_RIL
-XPCOMUtils.defineLazyServiceGetter(this, "ril",
+XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
                                    "@mozilla.org/ril/content-helper;1",
                                    "nsIRILContentHelper");
 #endif
 
 this.PhoneNumberUtils = {
   //  1. See whether we have a network mcc
   //  2. If we don't have that, look for the simcard mcc
-  //  3. TODO: If we don't have that or its 0 (not activated), pick up the last used mcc
+  //  3. If we don't have that or its 0 (not activated), pick up the last used mcc
   //  4. If we don't have, default to some mcc
 
   // mcc for Brasil
   _mcc: '724',
 
   getCountryName: function getCountryName() {
     let mcc;
     let countryName;
 
 #ifdef MOZ_B2G_RIL
     // Get network mcc
-    if (ril.voiceConnectionInfo && ril.voiceConnectionInfo.network) {
-      mcc = ril.voiceConnectionInfo.network.mcc;
+    let voice = mobileConnection.voiceConnectionInfo;
+    if (voice && voice.network && voice.network.mcc) {
+      mcc = voice.network.mcc;
     }
 
     // Get SIM mcc
-    if (!mcc) {
-      mcc = ril.iccInfo.mcc;
+    let iccInfo = mobileConnection.iccInfo;
+    if (!mcc && iccInfo.mcc) {
+      mcc = iccInfo.mcc;
     }
 
-    // Get previous mcc
-    if (!mcc && ril.voiceConnectionInfo) {
-      mcc = ril.voiceConnectionInfo.lastKnownMcc;
+    // Attempt to grab last known sim mcc from prefs
+    if (!mcc) {
+      try {
+        mcc = Services.prefs.getCharPref("ril.lastKnownSimMcc");
+      } catch (e) {}
     }
 
     // Set to default mcc
     if (!mcc) {
       mcc = this._mcc;
     }
 #else
     mcc = this._mcc;
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -115,35 +115,31 @@ function MobileICCCardLockResult(options
 MobileICCCardLockResult.prototype = {
   __exposedProps__ : {lockType: 'r',
                       enabled: 'r',
                       retryCount: 'r',
                       success: 'r'}
 };
 
 function MobileICCInfo() {
-  try {
-    this.lastKnownMcc = Services.prefs.getCharPref("ril.lastKnownMcc");
-  } catch (e) {}
 };
 MobileICCInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozMobileICCInfo]),
   classID:        MOBILEICCINFO_CID,
   classInfo:      XPCOMUtils.generateCI({
     classID:          MOBILEICCINFO_CID,
     classDescription: "MobileICCInfo",
     flags:            Ci.nsIClassInfo.DOM_OBJECT,
     interfaces:       [Ci.nsIDOMMozMobileICCInfo]
   }),
 
   // nsIDOMMozMobileICCInfo
 
   iccid: null,
   mcc: null,
-  lastKnownMcc: null,
   mnc: null,
   spn: null,
   msisdn: null
 };
 
 function VoicemailInfo() {}
 VoicemailInfo.prototype = {
   number: null,
@@ -427,19 +423,16 @@ RILContentHelper.prototype = {
     for (let key in srcInfo) {
       destInfo[key] = srcInfo[key];
     }
   },
 
   updateICCInfo: function updateICCInfo(srcInfo, destInfo) {
     for (let key in srcInfo) {
       destInfo[key] = srcInfo[key];
-      if (key === 'mcc') {
-        destInfo['lastKnownMcc'] = srcInfo[key];
-      }
     }
   },
 
   updateConnectionInfo: function updateConnectionInfo(srcInfo, destInfo) {
     for (let key in srcInfo) {
       if ((key != "network") && (key != "cell")) {
         destInfo[key] = srcInfo[key];
       }
@@ -990,21 +983,16 @@ RILContentHelper.prototype = {
         this.retryCount = msg.json.retryCount;
         if (this.cardState != msg.json.cardState) {
           this.cardState = msg.json.cardState;
           Services.obs.notifyObservers(null, kCardStateChangedTopic, null);
         }
         break;
       case "RIL:IccInfoChanged":
         this.updateICCInfo(msg.json, this.iccInfo);
-        if (this.iccInfo.mcc) {
-          try {
-            Services.prefs.setCharPref("ril.lastKnownMcc", this.iccInfo.mcc);
-          } catch (e) {}
-        }
         Services.obs.notifyObservers(null, kIccInfoChangedTopic, null);
         break;
       case "RIL:VoiceInfoChanged":
         this.updateConnectionInfo(msg.json, this.voiceConnectionInfo);
         Services.obs.notifyObservers(null, kVoiceChangedTopic, null);
         break;
       case "RIL:DataInfoChanged":
         this.updateConnectionInfo(msg.json, this.dataConnectionInfo);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1717,16 +1717,24 @@ RadioInterfaceLayer.prototype = {
                          oldIcc.msisdn != message.msisdn;
     if (!iccInfoChanged) {
       return;
     }
     // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
     // when the MCC or MNC codes have changed.
     this._sendTargetMessage("mobileconnection", "RIL:IccInfoChanged", message);
 
+    // Update lastKnownSimMcc.
+    if (message.mcc) {
+      try {
+        Services.prefs.setCharPref("ril.lastKnownSimMcc",
+                                   message.mcc.toString());
+      } catch (e) {}
+    }
+
     // Update lastKnownHomeNetwork.
     if (message.mcc && message.mnc) {
       try {
         Services.prefs.setCharPref("ril.lastKnownHomeNetwork",
                                    message.mcc + "-" + message.mnc);
         Services.prefs.setCharPref("ril.lastKnownNetwork",
                                      message.mcc + "-" + message.mnc);
       } catch (e) {}