Bug 955366 - Use Maps and Sets in IRC code, convert _participants. r=florian
authorPatrick Cloke <clokep@gmail.com>
Wed, 07 May 2014 20:19:05 -0400
changeset 16182 ffcaadc29781221084cc697e0c23a321fa33a4a5
parent 16181 9c05e894da1e6e96537892c3ff9988e06fbc1ecc
child 16183 da34b03c7e5718d4f1d10487d100dafc1d33d266
push id10115
push userclokep@gmail.com
push dateThu, 08 May 2014 14:05:31 +0000
treeherdercomm-central@da34b03c7e57 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersflorian
bugs955366
Bug 955366 - Use Maps and Sets in IRC code, convert _participants. r=florian
chat/modules/jsProtoHelper.jsm
chat/protocols/irc/irc.js
chat/protocols/irc/ircBase.jsm
chat/protocols/twitter/twitter.js
chat/protocols/xmpp/xmpp.jsm
chat/protocols/yahoo/yahoo.js
--- a/chat/modules/jsProtoHelper.jsm
+++ b/chat/modules/jsProtoHelper.jsm
@@ -519,17 +519,17 @@ const GenericConvIMPrototype = {
 };
 
 const GenericConvChatPrototype = {
   __proto__: GenericConversationPrototype,
   _interfaces: [Ci.prplIConversation, Ci.prplIConvChat],
   classDescription: "generic ConvChat object",
 
   _init: function(aAccount, aName, aNick) {
-    this._participants = {};
+    this._participants = new Map();
     this.nick = aNick;
     GenericConversationPrototype._init.call(this, aAccount, aName);
   },
 
   get isChat() true,
 
   _topic: "",
   _topicSetter: null,
@@ -596,19 +596,19 @@ const GenericConvChatPrototype = {
     if (aJoining == this._joining)
       return;
 
     this._joining = aJoining;
     this.notifyObservers(null, "update-conv-chatjoining");
   },
 
   getParticipants: function() {
+    // Convert the values of the Map into a nsSimpleEnumerator.
     return new nsSimpleEnumerator(
-      Object.keys(this._participants)
-            .map(function(key) this._participants[key], this)
+      [participant for (participant of this._participants.values())]
     );
   },
   getNormalizedChatBuddyName: function(aChatBuddyName) aChatBuddyName,
 
   writeMessage: function (aWho, aText, aProperties) {
     aProperties.containsNick =
       "incoming" in aProperties && this._pingRegexp.test(aText);
     GenericConversationPrototype.writeMessage.apply(this, arguments);
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -258,16 +258,17 @@ const GenericIRCConversation = {
     this._account.removeConversation(this.name);
     if (this._observedNicks.length)
       Services.obs.removeObserver(this, "user-info-received");
   }
 };
 
 function ircChannel(aAccount, aName, aNick) {
   this._init(aAccount, aName, aNick);
+  this._participants = new NormalizedMap(this.normalizeNick.bind(this));
   this._modes = new Set();
   this._observedNicks = [];
   this.banMasks = [];
   this._firstJoin = true;
 }
 ircChannel.prototype = {
   __proto__: GenericConvChatPrototype,
   _modes: null,
@@ -318,87 +319,81 @@ ircChannel.prototype = {
   unInit: function() {
     this.unInitIRCConversation();
     GenericConvChatPrototype.unInit.call(this);
   },
 
   // Use the normalized nick in order to properly notify the observers.
   getNormalizedChatBuddyName: function(aNick) this.normalizeNick(aNick),
 
-  hasParticipant: function(aNick)
-    hasOwnProperty(this._participants, this.normalizeNick(aNick)),
-
   getParticipant: function(aNick, aNotifyObservers) {
-    let normalizedNick = this.normalizeNick(aNick);
-    if (this.hasParticipant(aNick))
-      return this._participants[normalizedNick];
+    if (this._participants.has(aNick))
+      return this._participants.get(aNick);
 
     let participant = new ircParticipant(aNick, this);
-    this._participants[normalizedNick] = participant;
+    this._participants.set(aNick, participant);
 
     // Add the participant to the whois table if it is not already there.
     this._account.setWhois(participant._name);
 
     if (aNotifyObservers) {
       this.notifyObservers(new nsSimpleEnumerator([participant]),
                            "chat-buddy-add");
     }
     return participant;
   },
   updateNick: function(aOldNick, aNewNick) {
-    let isParticipant = this.hasParticipant(aOldNick);
+    let isParticipant = this._participants.has(aOldNick);
     if (this.normalizeNick(aOldNick) == this.normalizeNick(this.nick)) {
       // If this is the user's nick, change it.
       this.nick = aNewNick;
       // If the account was disconnected, it's OK the user is not a participant.
       if (!isParticipant)
         return;
     }
     else if (!isParticipant) {
       this.ERROR("Trying to rename nick that doesn't exist! " + aOldNick +
                  " to " + aNewNick);
       return;
     }
 
     // Get the original ircParticipant and then remove it.
     let participant = this.getParticipant(aOldNick);
-    this.removeParticipant(aOldNick);
+    this._participants.delete(aNick);
 
     // Update the nickname and add it under the new nick.
     participant._name = aNewNick;
-    this._participants[this.normalizeNick(aNewNick)] = participant;
+    this._participants.set(aNewNick, participant);
 
     this.notifyObservers(participant, "chat-buddy-update", aOldNick);
   },
-  removeParticipant: function(aNick, aNotifyObservers) {
-    if (!this.hasParticipant(aNick))
+  removeParticipant: function(aNick) {
+    if (!this._participants.has(aNick))
       return;
 
-    if (aNotifyObservers) {
-      let stringNickname = Cc["@mozilla.org/supports-string;1"]
-                              .createInstance(Ci.nsISupportsString);
-      stringNickname.data = aNick;
-      this.notifyObservers(new nsSimpleEnumerator([stringNickname]),
-                           "chat-buddy-remove");
-    }
-    delete this._participants[this.normalizeNick(aNick)];
+    let stringNickname = Cc["@mozilla.org/supports-string;1"]
+                           .createInstance(Ci.nsISupportsString);
+    stringNickname.data = aNick;
+    this.notifyObservers(new nsSimpleEnumerator([stringNickname]),
+                         "chat-buddy-remove");
+    this._participants.delete(aNick);
   },
   // Use this before joining to avoid errors of trying to re-add an existing
   // participant
   removeAllParticipants: function() {
     let stringNicknames = [];
-    for (let nickname in this._participants) {
+    this._participants.forEach(function(aParticipant) {
       let stringNickname = Cc["@mozilla.org/supports-string;1"]
                               .createInstance(Ci.nsISupportsString);
-      stringNickname.data = this._participants[nickname].name;
+      stringNickname.data = aParticipant.name;
       stringNicknames.push(stringNickname);
-    }
+    });
     this.notifyObservers(new nsSimpleEnumerator(stringNicknames),
                          "chat-buddy-remove");
-    this._participants = {};
+    this._participants.clear();
   },
 
   setMode: function(aNewMode, aModeParams, aSetter) {
     const hostMaskExp = /^.+!.+@.+$/;
     function getNextParam() {
       // If there's no next parameter, throw a warning.
       if (!aModeParams.length) {
         this.WARN("Mode parameter expected!");
@@ -425,17 +420,17 @@ ircChannel.prototype = {
     let userModes = {};
     let msg;
 
     for (let i = aNewMode.length - 1; i > 0; --i) {
       // Since some modes are conflicted between different server
       // implementations, check if a participant with that name exists. If this
       // is true, then update the mode of the ConvChatBuddy.
       if (this._account.memberStatuses.indexOf(aNewMode[i]) != -1 &&
-          aModeParams.length && this.hasParticipant(peekNextParam())) {
+          aModeParams.length && this._participants.has(peekNextParam())) {
         // Store the new modes for this nick (so each participant's mode is only
         // updated once).
         let nick = this.normalizeNick(getNextParam());
         if (!hasOwnProperty(userModes, nick))
           userModes[nick] = [];
         userModes[nick].push(aNewMode[i]);
 
         // Don't use this mode as a channel mode.
@@ -539,17 +534,17 @@ ircChannel.prototype = {
     if (this.topicSettable == this._previousTopicSettable &&
         this._previousTopicSettable != null)
       return;
 
     this.notifyObservers(this, "chat-update-topic");
   },
   get topicSettable() {
     // If we're not in the room yet, we don't exist.
-    if (!this.hasParticipant(this.nick))
+    if (!this._participants.has(this.nick))
       return false;
 
     // If the channel mode is +t, hops and ops can set the topic; otherwise
     // everyone can.
     let participant = this.getParticipant(this.nick);
     return this._modes.has("t") || participant.op || participant.halfOp;
   }
 };
@@ -1154,17 +1149,17 @@ ircAccount.prototype = {
         if (conversation.isChat)
           conversation.updateNick(aOldNick, aNewNick);
         conversation.writeMessage(aOldNick, msg, {system: true});
       }
     }
     else {
       msg = _("message.nick", aOldNick, aNewNick);
       for each (let conversation in this._conversations) {
-        if (conversation.isChat && conversation.hasParticipant(aOldNick)) {
+        if (conversation.isChat && conversation._participants.has(aOldNick)) {
           // Update the nick in every chat conversation it is in.
           conversation.updateNick(aOldNick, aNewNick);
           conversation.writeMessage(aOldNick, msg, {system: true});
         }
       }
     }
 
     // Adjust the whois table where necessary.
@@ -1672,17 +1667,17 @@ ircAccount.prototype = {
     // We must authenticate if we reconnect.
     delete this.isAuthenticated;
 
     // Clean up each conversation: mark as left and remove participant.
     for each (let conversation in this._conversations) {
       if (conversation.isChat && !conversation.left) {
         // Remove the user's nick and mark the conversation as left as that's
         // the final known state of the room.
-        conversation.removeParticipant(this._nickname, true);
+        conversation.removeParticipant(this._nickname);
         conversation.left = true;
       }
     }
 
     // If we disconnected during a pending LIST request, make sure callbacks
     // receive any remaining channels.
     if (this._pendingList)
       this._sendRemainingRoomInfo();
--- a/chat/protocols/irc/ircBase.jsm
+++ b/chat/protocols/irc/ircBase.jsm
@@ -79,17 +79,17 @@ function leftRoom(aAccount, aNicks, aCha
         msg = __(nick, true);
         // If the user left, mark the conversation as no longer being active.
         conversation.left = true;
       }
       else
         msg = __(nick);
 
       conversation.writeMessage(aSource, msg, {system: true});
-      conversation.removeParticipant(nick, true);
+      conversation.removeParticipant(nick);
     }
   }
   return true;
 }
 
 function writeMessage(aAccount, aMessage, aString, aType) {
   let type = {};
   type[aType] = true;
@@ -292,19 +292,19 @@ var ircBase = {
       if (quitMsg.startsWith("Quit: "))
         quitMsg = quitMsg.slice(6); // "Quit: ".length
       // If a quit message was included, show it.
       let msg = _("message.quit", aMessage.nickname,
                   quitMsg.length ? _("message.quit2", quitMsg) : "");
       // Loop over every conversation with the user and display that they quit.
       for each (let conversation in this._conversations) {
         if (conversation.isChat &&
-            conversation.hasParticipant(aMessage.nickname)) {
+            conversation._participants.has(aMessage.nickname)) {
           conversation.writeMessage(aMessage.servername, msg, {system: true});
-          conversation.removeParticipant(aMessage.nickname, true);
+          conversation.removeParticipant(aMessage.nickname);
         }
       }
 
       // Remove from the whois table.
       this.removeBuddyInfo(aMessage.nickname);
 
       // If the leaver is a buddy, mark as offline.
       let buddy = this.buddies.get(aMessage.nickname);
--- a/chat/protocols/twitter/twitter.js
+++ b/chat/protocols/twitter/twitter.js
@@ -336,21 +336,21 @@ Conversation.prototype = {
     flags.time = Math.round(new Date(aTweet.created_at) / 1000);
     flags._iconURL = aTweet.user.profile_image_url;
     if (text.contains("@" + this.nick))
       flags.containsNick = true;
 
     (new Tweet(aTweet, name, text, flags)).conversation = this;
   },
   _ensureParticipantExists: function(aNick) {
-    if (hasOwnProperty(this._participants, aNick))
+    if (this._participants.has(aNick))
       return;
 
     let chatBuddy = new ChatBuddy(aNick);
-    this._participants[aNick] = chatBuddy;
+    this._participants.set(aNick, chatBuddy);
     this.notifyObservers(new nsSimpleEnumerator([chatBuddy]),
                          "chat-buddy-add");
   },
   get name() this.nick + " timeline",
   get title() _("timeline", this.nick),
   get nick() this._account.name,
   set nick(aNick) {},
   get topicSettable() this.nick == this._account.name,
--- a/chat/protocols/xmpp/xmpp.jsm
+++ b/chat/protocols/xmpp/xmpp.jsm
@@ -96,37 +96,38 @@ const XMPPMUCConversationPrototype = {
     this._account.sendStanza(s);
   },
 
   /* Called by the account when a presence stanza is received for this muc */
   onPresenceStanza: function(aStanza) {
     let from = aStanza.attributes["from"];
     let nick = this._account._parseJID(from).resource;
     if (aStanza.attributes["type"] == "unavailable") {
-      if (!(nick in this._participants)) {
+      if (!this._participants.has(nick)) {
         this.WARN("received unavailable presence for an unknown MUC participant: " +
                   from);
         return;
       }
-      delete this._participants[nick];
+      this._participants.delete(nick);
       let nickString = Cc["@mozilla.org/supports-string;1"]
                          .createInstance(Ci.nsISupportsString);
       nickString.data = nick;
       this.notifyObservers(new nsSimpleEnumerator([nickString]),
                            "chat-buddy-remove");
       return;
     }
-    if (!hasOwnProperty(this._participants, nick)) {
-      this._participants[nick] = new MUCParticipant(nick, from, aStanza);
-      this.notifyObservers(new nsSimpleEnumerator([this._participants[nick]]),
+    if (!this._participants.get(nick)) {
+      let participant = new MUCParticipant(nick, from, aStanza);
+      this._participants.set(nick, participant);
+      this.notifyObservers(new nsSimpleEnumerator([participant]),
                            "chat-buddy-add");
     }
     else {
-      this._participants[nick].stanza = aStanza;
-      this.notifyObservers(this._participants[nick], "chat-buddy-update");
+      this._participants.get(nick).stanza = aStanza;
+      this.notifyObservers(this._participants.get(nick), "chat-buddy-update");
     }
   },
 
   /* Called by the account when a messsage is received for this muc */
   incomingMessage: function(aMsg, aStanza, aDate) {
     let from = this._account._parseJID(aStanza.attributes["from"]).resource;
     let flags = {};
     if (!from) {
--- a/chat/protocols/yahoo/yahoo.js
+++ b/chat/protocols/yahoo/yahoo.js
@@ -105,45 +105,44 @@ YahooConference.prototype = {
     this._account._session.sendConferenceMessage(this.getParticipantNames(),
                                                  this._roomName,
                                                  this._account.encodeMessage(aMsg));
   },
 
   addParticipant: function(aName) {
     // In case we receive multiple conference logon packets, prevent adding
     // duplicate buddies.
-    if (this._participants[aName])
+    if (this._participants.get(aName))
       return;
     let buddy = new YahooConferenceBuddy(aName, this);
-    this._participants[aName] = buddy;
+    this._participants.set(aName, buddy);
     this.notifyObservers(new nsSimpleEnumerator([buddy]), "chat-buddy-add");
     this.writeMessage(this._roomName,
                       _("system.message.conferenceLogon", aName),
                       {system: true});
   },
 
   removeParticipant: function(aName) {
     // In case we receive two logoff packets, make sure that the user is
     // actually here before continuing.
-    if (!this._participants[aName])
+    if (!this._participants.has(aName))
       return;
 
     let stringNickname = Cc["@mozilla.org/supports-string;1"]
                            .createInstance(Ci.nsISupportsString);
     stringNickname.data = aName;
     this.notifyObservers(new nsSimpleEnumerator([stringNickname]),
                          "chat-buddy-remove");
-    delete this._participants[aName];
+    this._participants.delete(aName);
     this.writeMessage(this._roomName,
                       _("system.message.conferenceLogoff", aName),
                       {system: true});
   },
 
-  getParticipantNames: function()
-    [this._participants[i].name for (i in this._participants)],
+  getParticipantNames: function() [p.name for (p in this._participants)]
 };
 YahooConference.prototype.__proto__ = GenericConvChatPrototype;
 
 function YahooConferenceBuddy(aName, aConference)
 {
   this._name = aName;
   this._conference = aConference;
 }