Bug 955553 - Provide a way to obtain the normalizedName of a nick, r=clokep,florian.
authorPatrick Cloke <clokep@gmail.com>
Wed, 27 Nov 2013 13:08:03 -0500
changeset 19243 f64f0684d462a47d4b0827104875395316b3d3ee
parent 19242 a4b5ecdce12f746b9ad51e17b03d97477b7cc693
child 19244 72787b02f400e7263ab620a1ebadb204019f7449
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)
reviewersclokep, florian
bugs955553
Bug 955553 - Provide a way to obtain the normalizedName of a nick, r=clokep,florian.
chat/components/public/imIAccount.idl
chat/modules/jsProtoHelper.jsm
chat/protocols/irc/irc.js
chat/protocols/twitter/twitter.js
chat/protocols/xmpp/xmpp.jsm
--- a/chat/components/public/imIAccount.idl
+++ b/chat/components/public/imIAccount.idl
@@ -123,16 +123,19 @@ interface prplIAccount: nsISupports {
   /*
    * Create a new chat conversation if it doesn't already exist.
    */
   void joinChat(in prplIChatRoomFieldValues aComponents);
 
   // A name that can be used to check for duplicates and is the basis
   // for the directory name for log storage.
   readonly attribute AUTF8String normalizedName;
+  // Request that the account normalizes a name. Use this only when an object
+  // providing a normalizedName doesn't exist yet or isn't accessible.
+  AUTF8String normalize(in AUTF8String aName);
 
   attribute purpleIProxyInfo proxyInfo;
 
   // protocol specific options: those functions set the protocol
   // specific options for the PurpleAccount
   void setBool(in string aName, in boolean aVal);
   void setInt(in string aName, in long aVal);
   void setString(in string aName, in AUTF8String aVal);
--- a/chat/modules/jsProtoHelper.jsm
+++ b/chat/modules/jsProtoHelper.jsm
@@ -208,17 +208,19 @@ const GenericAccountPrototype = {
              this.prefs.getComplexValue(aName, Ci.nsISupportsString).data :
              this.protocol._getOptionDefault(aName);
   },
 
   get prefs() this._prefs ||
     (this._prefs = Services.prefs.getBranch("messenger.account." +
                                             this.imAccount.id + ".options.")),
 
-  get normalizedName() this.name.toLowerCase(),
+  get normalizedName() this.normalize(this.name),
+  normalize: function(aName) aName.toLowerCase(),
+
   get proxyInfo() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
   set proxyInfo(val) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
 
   get HTMLEnabled() false,
   get HTMLEscapePlainText() false,
   get noBackgroundColors() true,
   get autoResponses() false,
   get singleFormatting() false,
@@ -274,18 +276,17 @@ const GenericAccountBuddyPrototype = {
       this._buddy.observe(this, "account-buddy-" + aTopic, aData);
     } catch(e) {
       this.ERROR(e);
     }
   },
 
   _userName: "",
   get userName() this._userName || this._buddy.userName,
-  get normalizedName()
-    this._userName ? this._userName.toLowerCase() : this._buddy.normalizedName,
+  get normalizedName() this._account.normalize(this.userName),
   _serverAlias: "",
   get serverAlias() this._serverAlias,
   set serverAlias(aNewAlias) {
     let old = this.displayName;
     this._serverAlias = aNewAlias;
     if (old != this.displayName)
       this._notifyObservers("display-name-changed", old);
   },
@@ -488,17 +489,17 @@ const GenericConversationPrototype = {
   },
 
   writeMessage: function(aWho, aText, aProperties) {
     (new Message(aWho, aText, aProperties)).conversation = this;
   },
 
   get account() this._account.imAccount,
   get name() this._name,
-  get normalizedName() this.name.toLowerCase(),
+  get normalizedName() this._account.normalize(this.name),
   get title() this.name,
   get startDate() this._date
 };
 
 const GenericConvIMPrototype = {
   __proto__: GenericConversationPrototype,
   _interfaces: [Ci.prplIConversation, Ci.prplIConvIM],
   classDescription: "generic ConvIM object",
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -536,19 +536,17 @@ ircChannel.prototype = {
     if (!this.hasParticipant(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.indexOf("t") == -1 || participant.op ||
            participant.halfOp;
-  },
-
-  get normalizedName() this._account.normalize(this.name)
+  }
 };
 copySharedBaseToPrototype(GenericIRCConversation, ircChannel.prototype);
 
 function ircParticipant(aName, aConv) {
   this._name = aName;
   this._conv = aConv;
   this._account = aConv._account;
   this._modes = [];
@@ -606,18 +604,16 @@ ircConversation.prototype = {
   // Overwrite the writeMessage function to apply CTCP formatting before
   // display.
   writeMessage: function(aWho, aText, aProperties) {
     GenericConvIMPrototype.writeMessage.call(this, aWho,
                                              ctcpFormatToHTML(aText),
                                              aProperties);
   },
 
-  get normalizedName() this._account.normalize(this.name),
-
   unInit: function() {
     this.unInitIRCConversation();
     GenericConvIMPrototype.unInit.call(this);
   },
 
   updateNick: function(aNewNick) {
     this._name = aNewNick;
     this.notifyObservers(null, "update-conv-title");
@@ -752,18 +748,16 @@ function ircAccountBuddy(aAccount, aBudd
 }
 ircAccountBuddy.prototype = {
   __proto__: GenericAccountBuddyPrototype,
 
   // Returns a list of imITooltipInfo objects to be displayed when the user
   // hovers over the buddy.
   getTooltipInfo: function() this._account.getBuddyInfo(this.normalizedName),
 
-  get normalizedName() this._account.normalize(this.userName),
-
   // Allow sending of messages to buddies even if they are not online since IRC
   // does not always provide status information in a timely fashion. (Note that
   // this is OK since the server will throw an error if the user is not online.)
   get canSendMessage() this.account.connected,
 
   // Called when the user wants to chat with the buddy.
   createConversation: function() this._account.createConversation(this.userName)
 };
@@ -806,18 +800,16 @@ ircAccount.prototype = {
   // The nickname stored in the account name.
   _accountNickname: null,
   // The nickname that will be used when connecting.
   _requestedNickname: null,
   // The prefix minus the nick (!user@host) as returned by the server, this is
   // necessary for guessing message lengths.
   prefix: null,
 
-  get normalizedName() this.normalize(this.name),
-
   // Parts of the specification give max lengths, keep track of them since a
   // server can overwrite them. The defaults given here are from RFC 2812.
   maxNicknameLength: 9, // 1.2.1 Users
   maxChannelLength: 50, // 1.3 Channels
   maxMessageLength: 512, // 2.3 Messages
   maxHostnameLength: 63, // 2.3.1 Message format in Augmented BNF
 
   // The default prefixes.
--- a/chat/protocols/twitter/twitter.js
+++ b/chat/protocols/twitter/twitter.js
@@ -124,17 +124,16 @@ function Conversation(aAccount)
   if (aAccount._userInfo.has(aAccount.name)) {
     let userInfo = aAccount._userInfo.get(aAccount.name);
     if ("description" in userInfo)
       this.setTopic(userInfo.description, aAccount.name, true);
   }
 }
 Conversation.prototype = {
   __proto__: GenericConvChatPrototype,
-  get normalizedName() this._account.normalize(this.name),
   unInit: function() {
     delete this._account._timeline;
     GenericConvChatPrototype.unInit.call(this);
   },
   inReplyToStatusId: null,
   startReply: function(aTweet) {
     this.inReplyToStatusId = aTweet.id_str;
     let entities = aTweet.entities;
@@ -366,21 +365,21 @@ function Account(aProtocol, aImAccount)
 {
   this._init(aProtocol, aImAccount);
   this._knownMessageIds = new Set();
   this._userInfo = new Map();
   this._friends = new Set();
 }
 Account.prototype = {
   __proto__: GenericAccountPrototype,
+
   // The correct normalization for twitter would be just toLowerCase().
   // Unfortunately, for backwards compatibility we retain this normalization,
   // which can cause edge cases for usernames with underscores.
   normalize: function(aString) aString.replace(/[^a-z0-9]/gi, "").toLowerCase(),
-  get normalizedName() this.normalize(this.name),
 
   consumerKey: Services.prefs.getCharPref("chat.twitter.consumerKey"),
   consumerSecret: Services.prefs.getCharPref("chat.twitter.consumerSecret"),
   completionURI: "http://oauthcallback.local/",
   baseURI: "https://api.twitter.com/",
   _lastMsgId: "",
 
   // Use this to keep track of the pending timeline requests. We attempt to fetch
--- a/chat/protocols/xmpp/xmpp.jsm
+++ b/chat/protocols/xmpp/xmpp.jsm
@@ -83,18 +83,16 @@ MUCParticipant.prototype = {
 // MUC (Multi-User Chat)
 const XMPPMUCConversationPrototype = {
   __proto__: GenericConvChatPrototype,
 
   _init: function(aAccount, aJID, aNick) {
     GenericConvChatPrototype._init.call(this, aAccount, aJID, aNick);
   },
 
-  get normalizedName() this.name,
-
   _targetResource: "",
 
   /* Called when the user enters a chat message */
   sendMsg: function (aMsg) {
     let s = Stanza.message(this.name, aMsg, null, {type: "groupchat"});
     this._account.sendStanza(s);
   },
 
@@ -329,18 +327,16 @@ const XMPPAccountBuddyPrototype = {
     if (this.subscription != "both") {
       tooltipInfo.push(new TooltipInfo(_("tooltip.subscription"),
                                        this.subscription));
     }
 
     return new nsSimpleEnumerator(tooltipInfo);
   },
 
-  get normalizedName() this.userName,
-
   // _rosterAlias is the value stored in the roster on the XMPP
   // server. For most servers we will be read/write.
   _rosterAlias: "",
   set rosterAlias(aNewAlias) {
     let old = this.displayName;
     this._rosterAlias = aNewAlias;
     if (old != this.displayName)
       this._notifyObservers("display-name-changed", old);
@@ -662,18 +658,16 @@ const XMPPAccountPrototype = {
     let password = aComponents.getValue("password");
     if (password) {
       x = Stanza.node("x", Stanza.NS.muc, null,
                       Stanza.node("password", null, null, password));
     }
     this._connection.sendStanza(Stanza.presence({to: jid + "/" + nick}, x));
   },
 
-  get normalizedName() this._normalizeJID(this.name),
-
   _idleSince: 0,
   observe: function(aSubject, aTopic, aData) {
     if (aTopic == "idle-time-changed") {
       let idleTime = parseInt(aData, 10);
       if (idleTime)
         this._idleSince = Math.floor(Date.now() / 1000) - idleTime;
       else
         delete this._idleSince;
@@ -747,17 +741,17 @@ const XMPPAccountPrototype = {
   disconnect: function() {
     this._disconnect();
   },
 
   addBuddy: function(aTag, aName) {
     if (!this._connection)
       throw "The account isn't connected";
 
-    let jid = this._normalizeJID(aName);
+    let jid = this.normalize(aName);
     if (!jid || !jid.contains("@"))
       throw "Invalid username";
 
     if (this._buddies.hasOwnProperty(jid)) {
       let subscription = this._buddies[jid].subscription;
       if (subscription && (subscription == "both" || subscription == "to")) {
         this.DEBUG("not re-adding an existing buddy");
         return;
@@ -832,17 +826,17 @@ const XMPPAccountPrototype = {
     }
   },
 
   /* Called when a presence stanza is received */
   onPresenceStanza: function(aStanza) {
     let from = aStanza.attributes["from"];
     this.DEBUG("Received presence stanza for " + from);
 
-    let jid = this._normalizeJID(from);
+    let jid = this.normalize(from);
     let type = aStanza.attributes["type"];
     if (type == "subscribe") {
       this.addBuddyRequest(jid,
                            this.replyToBuddyRequest.bind(this, "subscribed"),
                            this.replyToBuddyRequest.bind(this, "unsubscribed"));
     }
     else if (type == "unsubscribe" || type == "unsubscribed" ||
              type == "subscribed") {
@@ -860,23 +854,23 @@ const XMPPAccountPrototype = {
           this.ERROR("Failed to join MUC: " + aStanza.convertToString());
           return;
         }
         let nick = this._mucs[jid];
         this._mucs[jid] = new this._MUCConversationConstructor(this, jid, nick);
       }
       this._mucs[jid].onPresenceStanza(aStanza);
     }
-    else if (jid != this._normalizeJID(this._connection._jid.jid))
+    else if (jid != this.normalize(this._connection._jid.jid))
       this.WARN("received presence stanza for unknown buddy " + from);
   },
 
   /* Called when a message stanza is received */
   onMessageStanza: function(aStanza) {
-    let norm = this._normalizeJID(aStanza.attributes["from"]);
+    let norm = this.normalize(aStanza.attributes["from"]);
 
     let type = aStanza.attributes["type"];
     let body;
     let b = aStanza.getElement(["body"]);
     if (b) {
       // If there's a <body> child we have more than just typing notifications.
       // Prefer HTML (in <html><body>) and use plain text (<body>) as fallback.
       let htmlBody = aStanza.getElement(["html", "body"]);
@@ -946,17 +940,17 @@ const XMPPAccountPrototype = {
       aError = Ci.prplIAccount.ERROR_OTHER_ERROR;
     this._disconnect(aError, aException.toString());
   },
 
   /* Callbacks for Query stanzas */
   /* When a vCard is received */
   _vCardReceived: false,
   onVCard: function(aStanza) {
-    let jid = this._normalizeJID(aStanza.attributes["from"]);
+    let jid = this.normalize(aStanza.attributes["from"]);
     if (!jid || !this._buddies.hasOwnProperty(jid))
       return;
     let buddy = this._buddies[jid];
 
     let vCard = aStanza.getElement(["vCard"]);
     if (!vCard)
       return;
 
@@ -971,20 +965,21 @@ const XMPPAccountPrototype = {
       if (c.localName == "PHOTO")
         buddy._saveIcon(c);
     }
     if (!foundFormattedName && buddy._vCardFormattedName)
       buddy.vCardFormattedName = "";
     buddy._vCardReceived = true;
   },
 
-  _normalizeJID: function(aJID)
-    aJID.trim()
-        .split("/", 1)[0] // up to first slash
-        .toLowerCase(),
+  normalize: function(aJID) {
+    return aJID.trim()
+               .split("/", 1)[0] // up to first slash
+               .toLowerCase();
+  },
 
   _parseJID: function(aJid) {
     let match =
       /^(?:([^"&'/:<>@]+)@)?([^@/<>'\"]+)(?:\/(.*))?$/.exec(aJid);
     if (!match)
       return null;
 
     let result = {
@@ -1004,17 +999,17 @@ const XMPPAccountPrototype = {
   },
 
   _onRosterItem: function(aItem, aNotifyOfUpdates) {
     let jid = aItem.attributes["jid"];
     if (!jid) {
       this.WARN("Received a roster item without jid: " + aItem.getXML());
       return "";
     }
-    jid = this._normalizeJID(jid);
+    jid = this.normalize(jid);
 
     let subscription =  "";
     if ("subscription" in aItem.attributes)
       subscription = aItem.attributes["subscription"];
     if (subscription == "remove") {
       this._forgetRosterItem(jid);
       return "";
     }