Bug 955366 - Use Maps and Sets in IRC code, convert conversations. r=aleth
authorPatrick Cloke <clokep@gmail.com>
Mon, 19 May 2014 13:19:56 -0400
changeset 16207 9001c159773fb2ee682287bb70db0472aaafade9
parent 16206 6ee809fb4c458bbbf0b4c946c418c00d085c986e
child 16208 3cb370e6812d4a42337f53d55be8f10ec82b6930
push id10134
push userclokep@gmail.com
push dateMon, 19 May 2014 17:20:23 +0000
treeherdercomm-central@3cb370e6812d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaleth
bugs955366
Bug 955366 - Use Maps and Sets in IRC code, convert conversations. r=aleth
chat/protocols/irc/irc.js
chat/protocols/irc/ircBase.jsm
chat/protocols/irc/ircServices.jsm
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -776,17 +776,17 @@ ircAccountBuddy.prototype = {
     this._account.removeBuddy(this);
     GenericAccountBuddyPrototype.remove.call(this);
   }
 };
 
 function ircAccount(aProtocol, aImAccount) {
   this._init(aProtocol, aImAccount);
   this.buddies = new NormalizedMap(this.normalizeNick.bind(this));
-  this._conversations = {};
+  this.conversations = new NormalizedMap(this.normalize.bind(this));
 
   // Split the account name into usable parts.
   let splitter = this.name.lastIndexOf("@");
   this._accountNickname = this.name.slice(0, splitter);
   this._server = this.name.slice(splitter + 1);
 
   this._nickname = this._accountNickname;
   this._requestedNickname = this._nickname;
@@ -1139,46 +1139,46 @@ ircAccount.prototype = {
     return buddy;
   },
   changeBuddyNick: function(aOldNick, aNewNick) {
     let msg;
     if (this.normalizeNick(aOldNick) == this.normalizeNick(this._nickname)) {
       // Your nickname changed!
       this._nickname = aNewNick;
       msg = _("message.nick.you", aNewNick);
-      for each (let conversation in this._conversations) {
+      this.conversations.forEach(conversation => {
         // Update the nick for chats, and inform the user in every conversation.
         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) {
+      this.conversations.forEach(conversation => {
         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.
     this.removeBuddyInfo(aOldNick);
     this.setWhois(aNewNick);
 
     // If a private conversation is open with that user, change its title.
-    if (this.hasConversation(aOldNick)) {
+    if (this.conversations.has(aOldNick)) {
       // Get the current conversation and rename it.
       let conversation = this.getConversation(aOldNick);
 
       // Remove the old reference to the conversation and create a new one.
       this.removeConversation(aOldNick);
-      this._conversations[this.normalizeNick(aNewNick)] = conversation;
+      this.conversations.set(aNewNick, conversation);
 
       conversation.updateNick(aNewNick);
       conversation.writeMessage(aOldNick, msg, {system: true});
     }
   },
 
   /*
    * Generate a new nick to change to if the user requested nick is already in
@@ -1235,18 +1235,18 @@ ircAccount.prototype = {
     // Append the digits.
     newNick += newDigits;
 
     if (this.normalize(newNick) == this.normalize(this._nickname)) {
       // The nick we were about to try next is our current nick. This means
       // the user attempted to change to a version of the nick with a lower or
       // absent number suffix, and this failed.
       let msg = _("message.nick.fail", this._nickname);
-      for each (let conversation in this._conversations)
-        conversation.writeMessage(this._nickname, msg, {system: true});
+      this.conversations.forEach(conversation =>
+        conversation.writeMessage(this._nickname, msg, {system: true}));
       return true;
     }
 
     this.LOG(aOldNick + " is already in use, trying " + newNick);
     this.sendMessage("NICK", newNick); // Nick message.
     return true;
   },
 
@@ -1432,26 +1432,26 @@ ircAccount.prototype = {
   // each channel to enable later reconnections.
   _chatRoomFieldsList: {},
 
   // aComponents implements prplIChatRoomFieldValues.
   joinChat: function(aComponents) {
     let channel = aComponents.getValue("channel");
     if (!channel) {
       this.ERROR("joinChat called without a channel name.");
-      return;
+      return null;
     }
     // A channel prefix is required. If the user didn't include one,
     // we prepend # automatically to match the behavior of other
     // clients. Not doing it used to cause user confusion.
     if (this.channelPrefixes.indexOf(channel[0]) == -1)
       channel = "#" + channel;
 
     // No need to join a channel we are already in.
-    if (this.hasConversation(channel)) {
+    if (this.conversations.has(channel)) {
       let conv = this.getConversation(channel);
       if (!conv.left)
         return conv;
     }
 
     let params = [channel];
     let key = aComponents.getValue("password");
     if (key)
@@ -1479,36 +1479,32 @@ ircAccount.prototype = {
     if (params.length > 1)
       chatFields.password = params[1];
     return chatFields;
   },
 
   // Attributes
   get canJoinChat() true,
 
-  hasConversation: function(aConversationName)
-    hasOwnProperty(this._conversations, this.normalize(aConversationName)),
-
   // Returns a conversation (creates it if it doesn't exist)
   getConversation: function(aName) {
-    let name = this.normalize(aName);
-    if (!this.hasConversation(aName)) {
+    if (!this.conversations.has(aName)) {
       // If the whois information has been received, we have the proper nick
       // capitalization.
       if (this.whoisInformation.has(aName))
         aName = this.whoisInformation.get(aName).nick;
-      let constructor = this.isMUCName(aName) ? ircChannel : ircConversation;
-      this._conversations[name] = new constructor(this, aName, this._nickname);
+      let convClass = this.isMUCName(aName) ? ircChannel : ircConversation;
+      this.conversations.set(aName, new convClass(this, aName, this._nickname));
     }
-    return this._conversations[name];
+    return this.conversations.get(aName);
   },
 
   removeConversation: function(aConversationName) {
-    if (this.hasConversation(aConversationName))
-      delete this._conversations[this.normalize(aConversationName)];
+    if (this.conversations.has(aConversationName))
+      this.conversations.delete(aConversationName);
   },
 
   // This builds the message string that will be sent to the server.
   buildMessage: function(aCommand, aParams = []) {
     if (!aCommand) {
       this.ERROR("IRC messages must have a command.");
       return null;
     }
@@ -1662,43 +1658,42 @@ ircAccount.prototype = {
 
     clearTimeout(this._isOnTimer);
     delete this._isOnTimer;
 
     // 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) {
+    this.conversations.forEach(conversation => {
       if (conversation.isChat) {
         conversation.joining = false; // In case we never finished joining.
         if (!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);
           conversation.left = true;
         }
       }
-    }
+    });
 
     // If we disconnected during a pending LIST request, make sure callbacks
     // receive any remaining channels.
     if (this._pendingList)
       this._sendRemainingRoomInfo();
 
     // Clear whois table.
     this.whoisInformation.clear();
 
     this.reportDisconnected();
   },
 
   remove: function() {
-    for each (let conv in this._conversations)
-      conv.close();
-    delete this._conversations;
+    this.conversations.forEach(conv => conv.close());
+    delete this.conversations;
     this.buddies.forEach(function(aBuddy) aBuddy.remove());
     delete this.buddies;
   },
 
   unInit: function() {
     // Disconnect if we're online while this gets called.
     if (this._socket) {
       if (!this.disconnecting)
--- a/chat/protocols/irc/ircBase.jsm
+++ b/chat/protocols/irc/ircBase.jsm
@@ -65,17 +65,17 @@ function leftRoom(aAccount, aNicks, aCha
       return _(msgId2, aNick, aSource, reason);
     }
     if (aYou)
       return _(msgId2, reason);
     return _(msgId2, aNick, reason);
   }
 
   for each (let channelName in aChannels) {
-    if (!aAccount.hasConversation(channelName))
+    if (!aAccount.conversations.has(channelName))
       continue; // Handle when we closed the window
     let conversation = aAccount.getConversation(channelName);
     for each (let nick in aNicks) {
       let msg;
       if (aAccount.normalize(nick) == aAccount.normalize(aAccount._nickname)) {
         msg = __(nick, true);
         // If the user left, mark the conversation as no longer being active.
         conversation.left = true;
@@ -290,23 +290,23 @@ var ircBase = {
       // duplication and use a localized version.
       let quitMsg = aMessage.params[0] || "";
       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) {
+      this.conversations.forEach(conversation => {
         if (conversation.isChat &&
             conversation._participants.has(aMessage.nickname)) {
           conversation.writeMessage(aMessage.servername, msg, {system: 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);
       if (buddy)
         buddy.setStatus(Ci.imIStatusInfo.STATUS_OFFLINE, "");
@@ -346,20 +346,20 @@ var ircBase = {
       // If our status is Unavailable, tell the server.
       if (this.imAccount.statusInfo.statusType < Ci.imIStatusInfo.STATUS_AVAILABLE)
         this.observe(null, "status-changed");
 
       // Check if any of our buddies are online!
       this.sendIsOn();
 
       // Reconnect channels if they were not parted by the user.
-      for each (let conversation in this._conversations) {
+      this.conversations.forEach(conversation => {
         if (conversation.isChat && conversation._chatRoomFields)
           this.joinChat(conversation._chatRoomFields);
-      }
+      });
 
       return serverMessage(this, aMessage);
     },
     "002": function(aMessage) { // RPL_YOURHOST
       // Your host is <servername>, running version <ver>
       return serverMessage(this, aMessage);
     },
     "003": function(aMessage) { // RPL_CREATED
@@ -639,17 +639,17 @@ var ircBase = {
     /*
      * Status messages
      */
     "301": function(aMessage) { // RPL_AWAY
       // <nick> :<away message>
       // TODO set user as away on buddy list / conversation lists
       // TODO Display an autoResponse if this is after sending a private message
       // If the conversation is waiting for a response, it's received one.
-      if (this.hasConversation(aMessage.params[1]))
+      if (this.conversations.has(aMessage.params[1]))
         delete this.getConversation(aMessage.params[1])._pendingMessage;
       return this.setWhois(aMessage.params[1], {away: aMessage.params[2]});
     },
     "302": function(aMessage) { // RPL_USERHOST
       // :*1<reply> *( " " <reply )"
       // reply = nickname [ "*" ] "=" ( "+" / "-" ) hostname
       // TODO Can tell op / away from this
       return false;
@@ -1065,17 +1065,17 @@ var ircBase = {
 
       // Error messages, Implement Section 5.2 of RFC 2812
     "401": function(aMessage) { // ERR_NOSUCHNICK
       // <nickname> :No such nick/channel
       // Can arise in response to /mode, /invite, /kill, /msg, /whois.
       // TODO Handled in the conversation for /whois and /mgs so far.
       let msgId = "error.noSuch" +
         (this.isMUCName(aMessage.params[1]) ? "Channel" : "Nick");
-      if (this.hasConversation(aMessage.params[1])) {
+      if (this.conversations.has(aMessage.params[1])) {
         // If the conversation exists and we just sent a message from it, then
         // notify that the user is offline.
         if (this.getConversation(aMessage.params[1])._pendingMessage)
           conversationErrorMessage(this, aMessage, msgId);
       }
 
       return serverErrorMessage(this, aMessage, _(msgId, aMessage.params[1]));
     },
--- a/chat/protocols/irc/ircServices.jsm
+++ b/chat/protocols/irc/ircServices.jsm
@@ -130,17 +130,17 @@ var servicesBase = {
       // [<channel name>] <message>
       let channel = aMessage.params[1].split(" ", 1)[0];
       if (!channel || channel[0] != "[" || channel.slice(-1)[0] != "]")
         return false;
 
       // Remove the [ and ].
       channel = channel.slice(1, -1);
       // If it isn't a channel or doesn't exist, return early.
-      if (!this.isMUCName(channel) || !this.hasConversation(channel))
+      if (!this.isMUCName(channel) || !this.conversations.has(channel))
         return false;
 
       // Otherwise, display the message in that conversation.
       let params = {incoming: true};
       if (aMessage.command == "NOTICE")
         params.notification = true;
 
       // The message starts after the channel name, plus [, ] and a space.