Bug 1512788 - Keep address book listeners listening unless there are no extensions using them; r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Sun, 09 Dec 2018 20:02:21 +1300
changeset 33865 463d4f409e9a2d0e67befd639afce76dc3cef5e0
parent 33864 21219ad63ec84a637511fb8b76ddf34a874d51ca
child 33866 ffb6350a5d0cf0a5f2c813368c06473a54cd44e2
push id388
push userclokep@gmail.com
push dateMon, 28 Jan 2019 20:54:56 +0000
reviewersmkmelin
bugs1512788
Bug 1512788 - Keep address book listeners listening unless there are no extensions using them; r=mkmelin
mail/components/extensions/parent/ext-addressBook.js
--- a/mail/components/extensions/parent/ext-addressBook.js
+++ b/mail/components/extensions/parent/ext-addressBook.js
@@ -24,16 +24,20 @@ const hiddenProperties = [
  * Cache of items in the address book "tree". This cache is
  * completely blown away by most changes, so operations should
  * be as lightweight as possible.
  *
  * @implements {nsIAbListener}
  * @implements {nsIObserver}
  */
 var cache = new class extends EventEmitter {
+  constructor() {
+    super();
+    this.listenerCount = 0;
+  }
   _makeContactNode(contact, parent) {
     contact.QueryInterface(Ci.nsIAbCard);
     return {
       id: contact.UID,
       parentId: parent.UID,
       type: "contact",
       item: contact,
     };
@@ -250,32 +254,40 @@ var cache = new class extends EventEmitt
       }
       case "addrbook-list-member-added": {
         let parentNode = this.findMailingListById(data);
         this.emit("mailing-list-member-added", this._makeContactNode(subject, parentNode.item));
         break;
       }
     }
   }
+
+  incrementListeners() {
+    this.listenerCount++;
+    if (this.listenerCount == 1) {
+      MailServices.ab.addAddressBookListener(this, Ci.nsIAbListener.all);
+      Services.obs.addObserver(this, "addrbook-contact-created");
+      Services.obs.addObserver(this, "addrbook-contact-updated");
+      Services.obs.addObserver(this, "addrbook-list-updated");
+      Services.obs.addObserver(this, "addrbook-list-member-added");
+    }
+  }
+  decrementListeners() {
+    this.listenerCount--;
+    if (this.listenerCount == 0) {
+      MailServices.ab.removeAddressBookListener(this);
+      Services.obs.removeObserver(this, "addrbook-contact-created");
+      Services.obs.removeObserver(this, "addrbook-contact-updated");
+      Services.obs.removeObserver(this, "addrbook-list-updated");
+      Services.obs.removeObserver(this, "addrbook-list-member-added");
+    }
+  }
 };
-MailServices.ab.addAddressBookListener(cache, Ci.nsIAbListener.all);
-Services.obs.addObserver(cache, "addrbook-contact-created");
-Services.obs.addObserver(cache, "addrbook-contact-updated");
-Services.obs.addObserver(cache, "addrbook-list-updated");
-Services.obs.addObserver(cache, "addrbook-list-member-added");
 
 this.addressBook = class extends ExtensionAPI {
-  onShutdown() {
-    MailServices.ab.removeAddressBookListener(cache);
-    Services.obs.removeObserver(cache, "addrbook-contact-created");
-    Services.obs.removeObserver(cache, "addrbook-contact-updated");
-    Services.obs.removeObserver(cache, "addrbook-list-updated");
-    Services.obs.removeObserver(cache, "addrbook-list-member-added");
-  }
-
   getAPI(context) {
     return {
       addressBooks: {
         openUI() {
           let topWindow = Services.wm.getMostRecentWindow(AB_WINDOW_TYPE);
           if (!topWindow) {
             // TODO: wait until window is loaded before resolving
             topWindow = Services.ww.openWindow(null, AB_WINDOW_URI, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar", null);
@@ -312,46 +324,52 @@ this.addressBook = class extends Extensi
           context,
           name: "addressBooks.onCreated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("address-book-created", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("address-book-created", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onUpdated: new EventManager({
           context,
           name: "addressBooks.onUpdated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("address-book-updated", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("address-book-updated", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onDeleted: new EventManager({
           context,
           name: "addressBooks.onDeleted",
           register: fire => {
             let listener = (event, item) => {
               fire.sync(item.UID);
             };
 
             cache.on("address-book-deleted", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("address-book-deleted", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
       },
       contacts: {
         list(parentId) {
           let parentNode = cache.findAddressBookById(parentId);
           return cache.convert(parentNode.contacts, false);
@@ -394,46 +412,52 @@ this.addressBook = class extends Extensi
           context,
           name: "contacts.onCreated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("contact-created", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("contact-created", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onUpdated: new EventManager({
           context,
           name: "contacts.onUpdated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("contact-updated", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("contact-updated", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onDeleted: new EventManager({
           context,
           name: "contacts.onDeleted",
           register: fire => {
             let listener = (event, parent, item) => {
               fire.sync(parent.UID, item.UID);
             };
 
             cache.on("contact-deleted", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("contact-deleted", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
       },
       mailingLists: {
         list(parentId) {
           let parentNode = cache.findAddressBookById(parentId);
           return cache.convert(parentNode.mailingLists, false);
@@ -487,73 +511,83 @@ this.addressBook = class extends Extensi
           context,
           name: "mailingLists.onCreated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("mailing-list-created", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("mailing-list-created", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onUpdated: new EventManager({
           context,
           name: "mailingLists.onUpdated",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("mailing-list-updated", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("mailing-list-updated", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onDeleted: new EventManager({
           context,
           name: "mailingLists.onDeleted",
           register: fire => {
             let listener = (event, parent, item) => {
               fire.sync(parent.UID, item.UID);
             };
 
             cache.on("mailing-list-deleted", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("mailing-list-deleted", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onMemberAdded: new EventManager({
           context,
           name: "mailingLists.onMemberAdded",
           register: fire => {
             let listener = (event, node) => {
               fire.sync(cache.convert(node));
             };
 
             cache.on("mailing-list-member-added", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("mailing-list-member-added", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
         onMemberRemoved: new EventManager({
           context,
           name: "mailingLists.onMemberRemoved",
           register: fire => {
             let listener = (event, parent, item) => {
               fire.sync(parent.UID, item.UID);
             };
 
             cache.on("mailing-list-member-removed", listener);
+            cache.incrementListeners();
             return () => {
               cache.off("mailing-list-member-removed", listener);
+              cache.decrementListeners();
             };
           },
         }).api(),
       },
     };
   }
 };