Bug 519543 - Selecting on option for Search hangs thunderbird. r=bienvenu, a=blocking-thunderbird3.
authorAndrew Sutherland <asutherland@asutherland.org>
Sun, 11 Oct 2009 13:49:34 -0700
changeset 4117 5390b115f7eab8c97b5e4e26fa15d3e42f9062ad
parent 4116 91b098e16cb8e554cbc90f7e7e093394a3eef0ed
child 4118 18a99eeca8b22cc108384711ba66fd6eea4cb3b1
push idunknown
push userunknown
push dateunknown
reviewersbienvenu, blocking-thunderbird3
bugs519543
Bug 519543 - Selecting on option for Search hangs thunderbird. r=bienvenu, a=blocking-thunderbird3.
mailnews/db/gloda/modules/datamodel.js
mailnews/db/gloda/modules/index_ab.js
--- a/mailnews/db/gloda/modules/datamodel.js
+++ b/mailnews/db/gloda/modules/datamodel.js
@@ -643,20 +643,20 @@ function GlodaIdentity(aDatastore, aID, 
   this._datastore = aDatastore;
   this._id = aID;
   this._contactID = aContactID;
   this._contact = aContact;
   this._kind = aKind;
   this._value = aValue;
   this._description = aDescription;
   this._isRelay = aIsRelay;
-  /// cached positive indicator of a card.  false means we don't know, not that
-  ///  we are confident there is no card.  (Users may star contacts with
-  ///  frequency, we don't want to latch on an erroneous value.)
-  this._hasAddressBookCard = false;
+  /// Cached indication of whether there is an address book card for this
+  ///  identity.  We keep this up-to-date via address book listener
+  ///  notifications in |GlodaABIndexer|.
+  this._hasAddressBookCard = undefined;
 }
 
 GlodaIdentity.prototype = {
   NOUN_ID: 104,
   get id() { return this._id; },
   get contactID() { return this._contactID; },
   get contact() { return this._contact; },
   get kind() { return this._kind; },
@@ -678,32 +678,29 @@ GlodaIdentity.prototype = {
     return this.contact.name + " : " + this.value;
   },
 
   get abCard() {
     // for our purposes, the address book only speaks email
     if (this._kind != "email")
       return false;
     let card = GlodaUtils.getCardForEmail(this._value);
-    if (card)
-      this._hasAddressBookCard = true;
+    this._hasAddressBookCard = (card != null);
     return card;
   },
 
   /**
-   * Indicate whether this person is in the user's address book. This differs
-   *  from abCard in that its semantics are cheaper.  We can cache/flag that
-   *  the identity is in the address book on disk whereas we can't keep the
-   *  card reference live easily right now (until UUIDs happen...)
-   * However, we currently don't cache the value, lest it screw us.
+   * Indicates whether we have an address book card for this identity.  This
+   *  value is cached once looked-up and kept up-to-date by |GlodaABIndexer|
+   *  and its notifications.
    */
   get inAddressBook() {
-    if (this._hasAddressBookCard)
-      return true;
-    return this.abCard && true;
+    if (this._hasAddressBookCard !== undefined)
+      return this._hasAddressBookCard;
+    return (this.abCard && true) || false;
   },
 
   pictureURL: function(aSize) {
     if (this.inAddressBook) {
       // XXX should get the photo if we have it.
     }
     return "";
   }
--- a/mailnews/db/gloda/modules/index_ab.js
+++ b/mailnews/db/gloda/modules/index_ab.js
@@ -58,17 +58,20 @@ var GlodaABIndexer = {
   _log: null,
 
   name: "ab_indexer",
   enable: function() {
     if (this._log == null)
       this._log =  Log4Moz.repository.getLogger("gloda.ab_indexer");
 
     let abManager = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager);
-    abManager.addAddressBookListener(this, Ci.nsIAbListener.itemChanged);
+    abManager.addAddressBookListener(this,
+                                     Ci.nsIAbListener.itemAdded |
+                                       Ci.nsIAbListener.itemChanged |
+                                       Ci.nsIAbListener.directoryItemRemoved);
   },
 
   disable: function() {
     let abManager = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager);
     abManager.removeAddressBookListener(this);
   },
 
   get workers() {
@@ -84,35 +87,62 @@ var GlodaABIndexer = {
       query.kind("email");
       // we currently normalize all e-mail addresses to be lowercase
       query.value(card.primaryEmail.toLowerCase());
       let identityCollection = query.getCollection(aCallbackHandle);
       yield Gloda.kWorkAsync;
 
       if (identityCollection.items.length) {
         let identity = identityCollection.items[0];
+        // force the identity to know it has an associated ab card.
+        identity._hasAddressBookCard = true;
 
         this._log.debug("Found identity, processing card.");
         yield aCallbackHandle.pushAndGo(
             Gloda.grokNounItem(identity.contact, card, false, false,
                                aCallbackHandle));
         this._log.debug("Done processing card.");
       }
     }
 
     yield GlodaIndexer.kWorkDone;
   },
 
   initialSweep: function() {
   },
 
   /* ------ nsIAbListener ------ */
+  /**
+   * When an address book card is added, update the cached GlodaIdentity
+   *  object's cached idea of whether the identity has an ab card.
+   */
   onItemAdded: function ab_indexer_onItemAdded(aParentDir, aItem) {
+    if (!(aItem instanceof Ci.nsIAbCard))
+      return;
+
+    this._log.debug("Received Card Add Notification");
+    let identity = GlodaCollectionManager.cacheLookupOneByUniqueValue(
+      Gloda.NOUN_IDENTITY, "email@" + aItem.primaryEmail.toLowerCase());
+    if (identity)
+      identity._hasAddressBookCard = true;
   },
+  /**
+   * When an address book card is added, update the cached GlodaIdentity
+   *  object's cached idea of whether the identity has an ab card.
+   */
   onItemRemoved: function ab_indexer_onItemRemoved(aParentDir, aItem) {
+    if (!(aItem instanceof Ci.nsIAbCard))
+      return;
+
+    this._log.debug("Received Card Removal Notification");
+    let identity = GlodaCollectionManager.cacheLookupOneByUniqueValue(
+      Gloda.NOUN_IDENTITY, "email@" + aItem.primaryEmail.toLowerCase());
+    if (identity)
+      identity._hasAddressBookCard = false;
+
   },
   onItemPropertyChanged: function ab_indexer_onItemPropertyChanged(aItem,
       aProperty, aOldValue, aNewValue) {
     if (aProperty == null && aItem instanceof Ci.nsIAbCard) {
       this._log.debug("Received Card Change Notification");
 
       let card = aItem; // instanceof already QueryInterface'd for us.
       let job = new IndexingJob("ab-card", 1, card);