Move the notification code out of imWindows.jsm (new ibNotifications.jsm module).
authorFlorian Quèze <florian@instantbird.org>
Sat, 19 Mar 2011 00:27:17 +0100
changeset 18183 b1334d8d641a8c757f3877a3e8d501951c447058
parent 18182 a1765b4e2e9170c2e4b17903739f5304fdd69375
child 18184 1852dab926cbb0670ffc94917403233d0c686707
push id1103
push usermbanner@mozilla.com
push dateTue, 18 Mar 2014 07:44:06 +0000
treeherdercomm-beta@50c6279a0af0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
Move the notification code out of imWindows.jsm (new ibNotifications.jsm module).
im/modules/Makefile.in
im/modules/ibCore.jsm
im/modules/ibNotifications.jsm
im/modules/imWindows.jsm
--- a/im/modules/Makefile.in
+++ b/im/modules/Makefile.in
@@ -38,16 +38,17 @@ DEPTH		= ../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 	ibCore.jsm \
+	ibNotifications.jsm \
 	imContentSink.jsm \
 	imServices.jsm \
 	imSmileys.jsm \
 	imStatusUtils.jsm \
 	$(NULL)
 
 EXTRA_PP_JS_MODULES = \
 	imThemes.jsm \
--- a/im/modules/ibCore.jsm
+++ b/im/modules/ibCore.jsm
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 const EXPORTED_SYMBOLS = ["Core"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 Cu.import("resource:///modules/imServices.jsm");
 Cu.import("resource:///modules/imWindows.jsm");
+Cu.import("resource:///modules/ibNotifications.jsm");
 
 var Core = {
   _events: [
     "account-connected",
     "account-disconnected",
     "browser-request",
     "quit-application-requested"
   ],
@@ -108,16 +109,17 @@ var Core = {
         run: function(aMsg) {
           Services.core.setStatus(statusValue, aMsg);
           return true;
         }
       });
     }
 
     Conversations.init();
+    Notifications.init();
 
     this._events.forEach(function (aTopic) {
       Services.obs.addObserver(Core, aTopic, false);
     });
 
     this._showAccountManagerIfNeeded(true);
     return true;
   },
copy from im/modules/imWindows.jsm
copy to im/modules/ibNotifications.jsm
--- a/im/modules/imWindows.jsm
+++ b/im/modules/ibNotifications.jsm
@@ -30,147 +30,22 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CONVERSATION_WINDOW_URI = "chrome://instantbird/content/instantbird.xul";
-var EXPORTED_SYMBOLS = ["Conversations"];
+const EXPORTED_SYMBOLS = ["Notifications"];
 
 Components.utils.import("resource:///modules/imServices.jsm");
-
-var Conversations = {
-#ifdef XP_MACOSX
-  _badgeTimeout: null,
-  _showDockBadgePrefName: "messenger.options.showUnreadCountInDock",
-  get dockBadgeService() {
-    let badgeService =
-      Components.classes["@instantbird.org/purple/nsdockbadgeservice;1"]
-                .getService(Components.interfaces.nsIDockBadgeService);
-    delete this.dockBadgeService;
-    return this.dockBadgeService = badgeService;
-  },
-  _showUnreadCount: function c_showUnreadCount() {
-    let text = Conversations._unreadCount || "";
-    Conversations.dockBadgeService.badgeText = text;
-  },
-  _displayUnreadCountInDockBadge: function c_displayUnreadCountInDockBadge() {
-    if (!Services.prefs.getBoolPref(this._showDockBadgePrefName))
-      return;
-
-    if (this._unreadCount == 1 &&
-        Services.prefs.getBoolPref(this._getAttentionPrefName))
-      // We use a timeout because it looks better to add the dock
-      // badge only after the dock item has stopped jumping.
-      this._badgeTimeout =
-        this._windows[0].setTimeout(function () {
-          Conversations._badgeTimeout = null;
-          Conversations._showUnreadCount();
-        }, 1000);
-    else
-      if (!this._badgeTimeout)
-        this._showUnreadCount();
-  },
-  _hideUnreadCountDockBadge: function c_hideUnreadCountDockBadge() {
-    if (this._badgeTimeout) {
-      this._windows[0].clearTimeout(this._badgeTimeout);
-      this._badgeTimeout = null;
-    }
-    else
-      this.dockBadgeService.badgeText = "";
-  },
-#endif
-  _unreadCount: 0,
-  _incrementUnreadCount: function c_incrementUnreadCount() {
-    this._unreadCount++;
-#ifdef XP_MACOSX
-    this._displayUnreadCountInDockBadge();
-#endif
-  },
-  _clearUnreadCount: function c_clearUnreadCount() {
-    if (!this._unreadCount)
-      return;
+Components.utils.import("resource:///modules/imWindows.jsm");
 
-    this._unreadCount = 0;
-#ifdef XP_MACOSX
-    this._hideUnreadCountDockBadge();
-#endif
-  },
-  _windows: [],
-  _getAttentionPrefName: "messenger.options.getAttentionOnNewMessages",
-  _notificationPrefName: "messenger.options.notifyOfNewMessages",
-  registerWindow: function(aWindow) {
-    if (this._windows.indexOf(aWindow) == -1)
-      this._windows.unshift(aWindow);
-
-    if (this._pendingNotifications) {
-      // Cache in a variable and delete the existing notification array
-      // before redispatching the notifications so that the observe
-      // method can recreate it.
-      let notifications = this._pendingNotifications;
-      delete this._pendingNotifications;
-      notifications.forEach(function(aNotif) {
-        this.observe(aNotif.object, aNotif.topic, aNotif.msg);
-      }, this);
-    }
-  },
-  unregisterWindow: function(aWindow) {
-    let index = this._windows.indexOf(aWindow);
-    if (index != -1)
-      this._windows.splice(index, 1);
-  },
-
-  _purpleConv: {},
-  _conversations: [],
-  registerConversation: function(aConversation) {
-    if (this._conversations.indexOf(aConversation) == -1)
-      this._conversations.push(aConversation);
-
-    this._purpleConv[aConversation.conv.id] = aConversation;
-  },
-  unregisterConversation: function(aConversation) {
-    let index = this._conversations.indexOf(aConversation);
-    if (index != -1)
-      this._conversations.splice(index, 1);
-
-    if (this._purpleConv[aConversation.conv.id] == aConversation)
-      delete this._purpleConv[aConversation.conv.id];
-  },
-
-  focusConversation: function(aConv) {
-    let id = aConv.id;
-    if (id in this._purpleConv) {
-      let conv = this._purpleConv[id];
-      let doc = conv.ownerDocument;
-      doc.getElementById("conversations").selectedTab = conv.tab;
-      conv.focus();
-      doc.defaultView.focus();
-    }
-  },
-
-  get unreadConvsCount() {
-    return this._conversations.filter(function(conv) {
-      let tab = conv.tab;
-      return tab.hasAttribute("unread") &&
-             (!tab.hasAttribute("chat") || tab.hasAttribute("attention"));
-    }).length;
-  },
-
-  onWindowFocus: function (aWindow) {
-    let position = this._windows.indexOf(aWindow);
-    if (position != -1) {
-      this._windows.splice(position, 1);
-      this._windows.unshift(aWindow);
-    }
-    this._clearUnreadCount();
-  },
-
+var Notifications = {
   get ellipsis () {
     let ellipsis = "[\u2026]";
 
     try {
       ellipsis =
         Services.prefs.getComplexValue("intl.ellipsis",
                                        Components.interfaces.nsIPrefLocalizedString).data;
     } catch (e) { }
@@ -222,84 +97,25 @@ var Conversations = {
     // Finally show the notification!
     Components.classes["@mozilla.org/alerts-service;1"]
               .getService(Components.interfaces.nsIAlertsService)
               .showAlertNotification(icon, aMessage.alias || aMessage.who,
                                      messageText, true, "", observer);
   },
 
   init: function() {
-    let os = Services.obs;
-    ["new-text",
-     "new-conversation",
-     "purple-quit"].forEach(function (aTopic) {
-      os.addObserver(Conversations, aTopic, false);
-    });
-#ifdef XP_MACOSX
-    Services.prefs.addObserver(this._showDockBadgePrefName, this, false);
-#endif
+    Services.obs.addObserver(Notifications, "new-text", false);
   },
 
-  observe: function(aSubject, aTopic, aMsg) {
-    if (aTopic == "purple-quit") {
-      for (let id in this._purpleConv)
-        this._purpleConv[id].unInit();
-#ifdef XP_MACOSX
-      Services.prefs.removeObserver(Conversations._showDockBadgePrefName,
-                                    Conversations);
-#endif
-    }
-
-#ifdef XP_MACOSX
-    if (aTopic == "nsPref:changed") {
-      if (aMsg == this._showDockBadgePrefName) {
-        if (Services.prefs.getBoolPref(aMsg))
-          this._showUnreadCount();
-        else
-          this._hideUnreadCountDockBadge();
-      }
-      return;
-    }
-#endif
-
-    if (aTopic != "new-text" && aTopic != "new-conversation")
+  _notificationPrefName: "messenger.options.notifyOfNewMessages",
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic != "new-text")
       return;
 
-    let conv = aTopic == "new-conversation" ? aSubject : aSubject.conversation;
-    if (!(conv.id in this._purpleConv)) {
-      // The conversation is not displayed anywhere yet.
-      // First, check if an existing conversation window can accept it.
-      for each (let win in this._windows)
-        if (win.document.getElementById("conversations").addConversation(conv))
-          return;
-
-      // At this point, no existing registered window can accept the conversation.
-      // If we are already creating a window, append the notification.
-      if (this._pendingNotifications) {
-        this._pendingNotifications.push({object: aSubject, topic: aTopic,
-                                         msg: aMsg});
-        return;
-      }
+    if (!aSubject.incoming || aSubject.system ||
+        (aSubject.conversation.isChat && !aSubject.containsNick))
+      return;
 
-      // We need to create a new window.
-      Services.ww.openWindow(null, CONVERSATION_WINDOW_URI, "_blank",
-                             "chrome,toolbar,resizable", null);
-      this._pendingNotifications = [{object: aSubject, topic: aTopic, msg: aMsg}];
-      return;
-    }
-
-    if (aTopic == "new-text") {
-      let conv = this._purpleConv[aSubject.conversation.id];
-      if (!conv.loaded)
-        conv.addMsg(aSubject);
-      if (aSubject.incoming && !aSubject.system &&
-          (!aSubject.conversation.isChat || aSubject.containsNick)) {
-        if (Services.prefs.getBoolPref(this._getAttentionPrefName))
-          conv.ownerDocument.defaultView.getAttention();
-        if (!this._windows[0].document.hasFocus()) {
-          this._incrementUnreadCount();
-          if (Services.prefs.getBoolPref(this._notificationPrefName))
-            this._showMessageNotification(aSubject);
-        }
-      }
-    }
+    if (!Conversations.isConversationWindowFocused() &&
+        Services.prefs.getBoolPref(this._notificationPrefName))
+      this._showMessageNotification(aSubject);
   }
 };
--- a/im/modules/imWindows.jsm
+++ b/im/modules/imWindows.jsm
@@ -94,17 +94,16 @@ var Conversations = {
 
     this._unreadCount = 0;
 #ifdef XP_MACOSX
     this._hideUnreadCountDockBadge();
 #endif
   },
   _windows: [],
   _getAttentionPrefName: "messenger.options.getAttentionOnNewMessages",
-  _notificationPrefName: "messenger.options.notifyOfNewMessages",
   registerWindow: function(aWindow) {
     if (this._windows.indexOf(aWindow) == -1)
       this._windows.unshift(aWindow);
 
     if (this._pendingNotifications) {
       // Cache in a variable and delete the existing notification array
       // before redispatching the notifications so that the observe
       // method can recreate it.
@@ -133,16 +132,18 @@ var Conversations = {
     let index = this._conversations.indexOf(aConversation);
     if (index != -1)
       this._conversations.splice(index, 1);
 
     if (this._purpleConv[aConversation.conv.id] == aConversation)
       delete this._purpleConv[aConversation.conv.id];
   },
 
+  isConversationWindowFocused: function()
+    this._windows.length > 0 && this._windows[0].document.hasFocus(),
   focusConversation: function(aConv) {
     let id = aConv.id;
     if (id in this._purpleConv) {
       let conv = this._purpleConv[id];
       let doc = conv.ownerDocument;
       doc.getElementById("conversations").selectedTab = conv.tab;
       conv.focus();
       doc.defaultView.focus();
@@ -161,76 +162,16 @@ var Conversations = {
     let position = this._windows.indexOf(aWindow);
     if (position != -1) {
       this._windows.splice(position, 1);
       this._windows.unshift(aWindow);
     }
     this._clearUnreadCount();
   },
 
-  get ellipsis () {
-    let ellipsis = "[\u2026]";
-
-    try {
-      ellipsis =
-        Services.prefs.getComplexValue("intl.ellipsis",
-                                       Components.interfaces.nsIPrefLocalizedString).data;
-    } catch (e) { }
-    return ellipsis;
-  },
-
-  _showMessageNotification: function (aMessage) {
-    // Get the hidden window. We need it to create a DOM node.
-    let win = Components.classes["@mozilla.org/appshell/appShellService;1"]
-                        .getService(Components.interfaces.nsIAppShellService)
-                        .hiddenDOMWindow;
-
-    // Put the message content into a div node.
-    let xhtmlElement = win.document.createElementNS("http://www.w3.org/1999/xhtml", "div");
-    xhtmlElement.innerHTML = aMessage.message;
-
-    // Convert the div node content to plain text.
-    let encoder =
-      Components.classes["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
-                .createInstance(Components.interfaces.nsIDocumentEncoder);
-    encoder.init(win.document, "text/plain", 0);
-    encoder.setNode(xhtmlElement);
-    let messageText = encoder.encodeToString().replace(/\s+/g, " ");
-
-    // Crop the end of the text if needed.
-    if (messageText.length > 50)
-      messageText = messageText.substr(0, 50) + this.ellipsis;
-
-    // Use the buddy icon if available for the icon of the notification.
-    let icon;
-    let conv = aMessage.conversation;
-    if (!conv.isChat) {
-      let buddy = conv.buddy;
-      if (buddy)
-        icon = buddy.buddyIconFilename;
-    }
-    if (!icon)
-      icon = "chrome://instantbird/skin/newMessage.png";
-
-    // Prepare an observer to focus the conversation if the
-    // notification is clicked.
-    let observer = {
-      observe: function(aSubject, aTopic, aData) {
-        if (aTopic == "alertclickcallback")
-          Conversations.focusConversation(aMessage.conversation);
-      }
-    };
-
-    // Finally show the notification!
-    Components.classes["@mozilla.org/alerts-service;1"]
-              .getService(Components.interfaces.nsIAlertsService)
-              .showAlertNotification(icon, aMessage.alias || aMessage.who,
-                                     messageText, true, "", observer);
-  },
-
   init: function() {
     let os = Services.obs;
     ["new-text",
      "new-conversation",
      "purple-quit"].forEach(function (aTopic) {
       os.addObserver(Conversations, aTopic, false);
     });
 #ifdef XP_MACOSX
@@ -289,17 +230,14 @@ var Conversations = {
     if (aTopic == "new-text") {
       let conv = this._purpleConv[aSubject.conversation.id];
       if (!conv.loaded)
         conv.addMsg(aSubject);
       if (aSubject.incoming && !aSubject.system &&
           (!aSubject.conversation.isChat || aSubject.containsNick)) {
         if (Services.prefs.getBoolPref(this._getAttentionPrefName))
           conv.ownerDocument.defaultView.getAttention();
-        if (!this._windows[0].document.hasFocus()) {
+        if (!this.isConversationWindowFocused())
           this._incrementUnreadCount();
-          if (Services.prefs.getBoolPref(this._notificationPrefName))
-            this._showMessageNotification(aSubject);
-        }
       }
     }
   }
 };