[tor-commits] [tor-messenger-build/master] Rebase the preparation patch

arlo at torproject.org arlo at torproject.org
Thu Nov 27 03:32:10 UTC 2014


commit 6e77842b91dae35d0dc05adde4cf96ff0533fc42
Author: Arlo Breault <arlolra at gmail.com>
Date:   Wed Nov 26 19:31:11 2014 -0800

    Rebase the preparation patch
    
     * And add the tests patch for good measure.
---
 projects/instantbird/config                        |    5 +-
 .../prepare-messages-for-displaying.patch          |   40 ++-
 projects/instantbird/test-transformation-api.patch |  367 ++++++++++++++++++++
 3 files changed, 392 insertions(+), 20 deletions(-)

diff --git a/projects/instantbird/config b/projects/instantbird/config
index f49d4f8..ce37906 100644
--- a/projects/instantbird/config
+++ b/projects/instantbird/config
@@ -56,14 +56,15 @@ input_files:
     project: mozilla
     pkg_type: src
   - filename: preferences.patch
-  - filename: irc.patch
+  - filename: prepare-messages-for-displaying.patch
+  - filename: test-transformation-api.patch
   - filename: slashme.patch
+  - filename: irc.patch
   - filename: xmpp.patch
   - filename: facebook.patch
   - filename: accountcreation.patch
   - filename: links.patch
   - filename: picture.patch
-  - filename: prepare-messages-for-displaying.patch
   - filename: spi-cacert.der
   - filename: ccc-jabber-cacert.der
   - filename: fix-mingw-build.nsprpatch
diff --git a/projects/instantbird/prepare-messages-for-displaying.patch b/projects/instantbird/prepare-messages-for-displaying.patch
index 1796a0c..4828728 100644
--- a/projects/instantbird/prepare-messages-for-displaying.patch
+++ b/projects/instantbird/prepare-messages-for-displaying.patch
@@ -1,9 +1,9 @@
 # HG changeset patch
 # User Arlo Breault <arlolra at gmail.com>
-# Date 1411421387 25200
-#      Mon Sep 22 14:29:47 2014 -0700
-# Node ID fbe43529c92d26a1c7674fc964ae5e1aadc9ac2f
-# Parent  2b6f0a6859958c9b57b3975001ee7aac257d9ae8
+# Date 1417058407 28800
+#      Wed Nov 26 19:20:07 2014 -0800
+# Node ID 0108590a22b0943081fe0f26375834da2f84b0b6
+# Parent  e4c698528d69e8d4e2116bbf2e28f91fd5b5f972
 Prepare messages for displaying
 
  * Adds a converse method to prepareForSending.
@@ -56,7 +56,7 @@ diff --git a/chat/components/public/prplIConversation.idl b/chat/components/publ
   * This is the XPCOM purple conversation component, a proxy for PurpleConversation.
   */
  
-@@ -47,16 +48,19 @@ interface prplIConversation: nsISupports
+@@ -47,16 +48,21 @@ interface prplIConversation: nsISupports
    void sendMsg(in AUTF8String aMsg);
  
    /* Preprocess messages before they are sent (eg. split long messages).
@@ -65,7 +65,9 @@ diff --git a/chat/components/public/prplIConversation.idl b/chat/components/publ
                           [optional] out unsigned long aMsgCount,
                           [retval, array, size_is(aMsgCount)] out wstring aMsgs);
  
-+  /* Postprocess messages before they are displayed (eg. escaping). */
++  /* Postprocess messages before they are displayed (eg. escaping). The
++     implementation can set aMsg.displayMessage, otherwise the originalMessage
++     is used. */
 +  void prepareForDisplaying(in imIMessage aMsg);
 +
    /* Send information about the current typing state to the server.
@@ -79,7 +81,7 @@ diff --git a/chat/components/public/prplIConversation.idl b/chat/components/publ
 diff --git a/chat/components/src/imConversations.js b/chat/components/src/imConversations.js
 --- a/chat/components/src/imConversations.js
 +++ b/chat/components/src/imConversations.js
-@@ -399,16 +399,17 @@ UIConversation.prototype = {
+@@ -403,16 +403,17 @@ UIConversation.prototype = {
      this._observers = this._observers.filter(function(o) o !== aObserver);
    },
    notifyObservers: function(aSubject, aTopic, aData) {
@@ -97,7 +99,7 @@ diff --git a/chat/components/src/imConversations.js b/chat/components/src/imConv
          if (!this.isChat || aSubject.containsNick)
            ++this._unreadTargetedMessageCount;
        }
-@@ -426,16 +427,20 @@ UIConversation.prototype = {
+@@ -430,16 +431,21 @@ UIConversation.prototype = {
        if (aSubject.incoming && !aSubject.system &&
            (!this.isChat || aSubject.containsNick)) {
          this.notifyObservers(aSubject, "new-directed-incoming-message", aData);
@@ -106,8 +108,9 @@ diff --git a/chat/components/src/imConversations.js b/chat/components/src/imConv
      }
    },
  
-+  // Called above when the conversation service itself is set as the
-+  // conversation for a message. This is case for some system messages.
++  // Used above when notifying of new-texts originating in the
++  // UIConversation. The happens when this.systemMessage() is called. The
++  // conversation for the message is set as the UIConversation. 
 +  prepareForDisplaying: function(aMsg) {},
 +
    // prplIConvIM
@@ -142,7 +145,7 @@ diff --git a/chat/modules/jsProtoHelper.jsm b/chat/modules/jsProtoHelper.jsm
 diff --git a/chat/protocols/irc/irc.js b/chat/protocols/irc/irc.js
 --- a/chat/protocols/irc/irc.js
 +++ b/chat/protocols/irc/irc.js
-@@ -129,16 +129,20 @@ const GenericIRCConversation = {
+@@ -126,16 +126,20 @@ const GenericIRCConversation = {
    getMaxMessageLength: function() {
      // Build the shortest possible message that could be sent to other users.
      let baseMessage = ":" + this._account._nickname + this._account.prefix +
@@ -163,13 +166,13 @@ diff --git a/chat/protocols/irc/irc.js b/chat/protocols/irc/irc.js
  
      // Attempt to smartly split a string into multiple lines (based on the
      // maximum number of characters the message can contain).
-@@ -278,24 +282,16 @@ ircChannel.prototype = {
+@@ -274,24 +278,16 @@ ircChannel.prototype = {
    _receivedInitialMode: false,
    // For IRC you're not in a channel until the JOIN command is received, open
    // all channels (initially) as left.
    _left: true,
-   // True until successfully joined for the first time.
-   _firstJoin: false,
+   // True while we are rejoining a channel previously parted by the user.
+   _rejoined: false,
    banMasks: [],
  
 -  // Overwrite the writeMessage function to apply CTCP formatting before
@@ -188,7 +191,7 @@ diff --git a/chat/protocols/irc/irc.js b/chat/protocols/irc/irc.js
      // Otherwise, fall back to the default part message, if it exists.
      let msg = aMessage || this._account.getString("partmsg");
      if (msg)
-@@ -599,24 +595,16 @@ function ircConversation(aAccount, aName
+@@ -595,24 +591,16 @@ function ircConversation(aAccount, aName
    // Always request the info as it may be out of date.
    this._waitingForNick = true;
    this.requestBuddyInfo(aName);
@@ -238,7 +241,7 @@ diff --git a/chat/protocols/xmpp/xmpp.jsm b/chat/protocols/xmpp/xmpp.jsm
   *          owner     -> founder
   */
  const kRoles = ["outcast", "visitor", "participant", "member", "moderator",
-@@ -255,16 +260,22 @@ const XMPPConversationPrototype = {
+@@ -255,16 +260,23 @@ const XMPPConversationPrototype = {
        who = this._account._connection._jid.jid;
      if (!who)
        who = this._account.name;
@@ -247,7 +250,8 @@ diff --git a/chat/protocols/xmpp/xmpp.jsm b/chat/protocols/xmpp/xmpp.jsm
      delete this._typingState;
    },
  
-+  /* Perform entity escaping before displaying the message. */
++  /* Perform entity escaping before displaying the message. We assume incoming
++     messages have already been escaped, and will otherwise be filtered. */
 +  prepareForDisplaying: function(aMsg) {
 +    if (aMsg.outgoing && !aMsg.system)
 +      aMsg.displayMessage = TXTToHTML(aMsg.displayMessage);
@@ -261,7 +265,7 @@ diff --git a/chat/protocols/xmpp/xmpp.jsm b/chat/protocols/xmpp/xmpp.jsm
      if (aStanza.attributes["type"] == "error") {
        aMsg = _("conversation.error.notDelivered", aMsg);
        flags.system = true;
-@@ -889,19 +900,17 @@ const XMPPAccountPrototype = {
+@@ -889,19 +901,17 @@ const XMPPAccountPrototype = {
        // Prefer HTML (in <html><body>) and use plain text (<body>) as fallback.
        let htmlBody = aStanza.getElement(["html", "body"]);
        if (htmlBody)
diff --git a/projects/instantbird/test-transformation-api.patch b/projects/instantbird/test-transformation-api.patch
new file mode 100644
index 0000000..f43fcbc
--- /dev/null
+++ b/projects/instantbird/test-transformation-api.patch
@@ -0,0 +1,367 @@
+# HG changeset patch
+# User Arlo Breault <arlolra at gmail.com>
+# Date 1414370632 25200
+#      Sun Oct 26 17:43:52 2014 -0700
+# Node ID 49fc79e39b9c292d182a46aa800135bd65b6c38f
+# Parent  030063bcb41284997f862b9b2d12e93bfc0b121a
+Add tests for new messaging pipeline
+
+ * Tests for bugs 1071166, 1088772, and 983347.
+
+diff --git a/chat/components/public/imIConversationsService.idl b/chat/components/public/imIConversationsService.idl
+--- a/chat/components/public/imIConversationsService.idl
++++ b/chat/components/public/imIConversationsService.idl
+@@ -70,29 +70,31 @@ interface imIConversationsService: nsISu
+ };
+ 
+ // Because of limitations in libpurple (write_conv is called without context),
+ // there's an implicit contract that whatever message string the conversation
+ // service passes to a protocol, it'll get back as the originalMessage when
+ // "new-text" is notified. This is required for the OTR extensions to work.
+ 
+ // A cancellable outgoing message. Before handing a message off to a protocol,
+-// the conversation service notifies observers (typically add-ons) of an
+-// outgoing message, which can be transformed or cancelled.
++// the conversation service notifies observers of `preparing-message` and
++// `sending-message` (typically add-ons) of an outgoing message, which can be
++// transformed or cancelled.
+ [scriptable, uuid(4391ba5c-9566-41a9-bb9b-fd0a0a490c2c)]
+ interface imIOutgoingMessage: nsISupports {
+            attribute AUTF8String message;
+            attribute boolean cancelled;
+   readonly attribute prplIConversation conversation;
+ };
+ 
+ // A cancellable message to be displayed. When the conversation service is
+-// notified of a new-text (ie. an incoming or outgoing message to be displayed),
+-// it in turn notifies observers (again, typically add-ons), which have the
+-// opportunity to swap or cancel the message.
++// notified of a `new-text` (ie. an incoming or outgoing message to be
++// displayed), it in turn notifies observers of `received-message`
++// (again, typically add-ons), which have the opportunity to swap or cancel
++// the message.
+ [scriptable, uuid(3f88cc5c-6940-4eb5-a576-c65770f49ce9)]
+ interface imIMessage: prplIMessage {
+            attribute boolean cancelled;
+            // Holds the sender color for Chats.
+            // Empty string by default, it is set by the conversation binding.
+            attribute AUTF8String color;
+ 
+            // What eventually gets shown to the user.
+diff --git a/chat/components/src/imConversations.js b/chat/components/src/imConversations.js
+--- a/chat/components/src/imConversations.js
++++ b/chat/components/src/imConversations.js
+@@ -59,16 +59,17 @@ imMessage.prototype = {
+   get containsNick() this.prplMessage.containsNick,
+   get noLog() this.prplMessage.noLog,
+   get error() this.prplMessage.error,
+   get delayed() this.prplMessage.delayed,
+   get noFormat() this.prplMessage.noFormat,
+   get containsImages() this.prplMessage.containsImages,
+   get notification() this.prplMessage.notification,
+   get noLinkification() this.prplMessage.noLinkification,
++  get originalMessage() this.prplMessage.originalMessage,
+   getActions: function(aCount) this.prplMessage.getActions(aCount || {})
+ };
+ 
+ function UIConversation(aPrplConversation)
+ {
+   this._prplConv = {};
+   this.id = ++gLastUIConvId;
+   this._observers = [];
+@@ -120,17 +121,21 @@ UIConversation.prototype = {
+   _currentTargetId: 0,
+   changeTargetTo: function(aPrplConversation) {
+     let id = aPrplConversation.id;
+     if (this._currentTargetId == id)
+       return;
+ 
+     if (!(id in this._prplConv)) {
+       this._prplConv[id] = aPrplConversation;
+-      aPrplConversation.addObserver(this.observeConv.bind(this, id));
++      // Pass an object here, instead of just a function, because coercion
++      // to an nsIObserver won't happen in the JS tests.
++      aPrplConversation.addObserver({
++        observe: this.observeConv.bind(this, id)
++      });
+     }
+ 
+     let shouldNotify = this._currentTargetId;
+     this._currentTargetId = id;
+     if (!this.isChat) {
+       let buddy = this.buddy;
+       if (buddy)
+         ({statusType: this.statusType, statusText: this.statusText}) = buddy;
+@@ -356,17 +361,17 @@ UIConversation.prototype = {
+ 
+     // Protocols have an opportunity here to preprocess messages before they are
+     // sent (eg. split long messages). If a message is split here, the split
+     // will be visible in the UI.
+     let messages = this.target.prepareForSending(om);
+ 
+     // Protocols can return null if they don't need to make any changes.
+     // (nb. passing null with retval array results in an empty array)
+-    if (!messages.length)
++    if (!messages || !messages.length)
+       messages = [om.message];
+ 
+     for (let msg of messages) {
+       // Add-ons (eg. OTR) have an opportunity to tweak or cancel the message
+       // at this point.
+       om = new OutgoingMessage(msg, this.target);
+       this.notifyObservers(om, "sending-message");
+       if (om.cancelled)
+diff --git a/chat/components/src/test/test_conversations.js b/chat/components/src/test/test_conversations.js
+new file mode 100644
+--- /dev/null
++++ b/chat/components/src/test/test_conversations.js
+@@ -0,0 +1,235 @@
++/* Any copyright is dedicated to the Public Domain.
++ * http://creativecommons.org/publicdomain/zero/1.0/ */
++
++const Cu = Components.utils;
++Cu.import("resource:///modules/imServices.jsm");
++Cu.import("resource:///modules/jsProtoHelper.jsm");
++
++let imConversations = {};
++Services.scriptloader.loadSubScript(
++  "resource:///components/imConversations.js", imConversations
++);
++
++// Fake prplConversation
++let _id = 0;
++function Conversation(aName) {
++  this._name = aName;
++  this._observers = [];
++  this._date = Date.now() * 1000;
++  this.id = ++_id;
++}
++Conversation.prototype = {
++  __proto__: GenericConvIMPrototype,
++  _account: {
++    imAccount: {
++      protocol: {name: "Fake Protocol"},
++      alias: "",
++      name: "Fake Account"
++    },
++    ERROR: function(e) {throw e;}
++  }
++};
++
++// Ensure that when iMsg.message is set to a message (including the empty
++// string), it returns that message. If not, it should return the original
++// message. This prevents regressions due to JS coercions.
++let test_null_message = function() {
++  let originalMessage = "Hi!";
++  let pMsg = new Message("buddy", originalMessage, {
++    outgoing: true, _alias: "buddy", time: Date.now()
++  });
++  let iMsg = new imConversations.imMessage(pMsg);
++  equal(iMsg.message, originalMessage);
++  // Setting the message should prevent a fallback to the original.
++  iMsg.message = "";
++  equal(iMsg.message, "");
++  equal(iMsg.originalMessage, originalMessage);
++};
++
++// ROT13, used as an example transformation.
++function rot13(aString) {
++  return aString.replace(/[a-zA-Z]/g, function(c) {
++    return String.fromCharCode(c.charCodeAt(0) + (c.toLowerCase() < "n" ? 1 : -1) * 13);
++  });
++}
++
++// A test that exercises the message transformation pipeline.
++//
++// From the sending users perspective, this looks like:
++//   -> UIConv sendMsg
++//   -> UIConv notifyObservers `preparing-message`
++//   -> protocol prepareForSending
++//   -> UIConv notifyObservers `sending-message`
++//   -> protocol sendMsg
++//   -> protocol writeMessage
++//   -> protocol notifyObservers `new-text`
++//   -> UIConv notifyObservers `received-message`
++//   -> protocol prepareForDisplaying
++//   -> UIConv notifyObservers `new-text`
++//
++// From the receiving users perspective, they get:
++//   -> protocol writeMessage
++//   -> protocol notifyObservers `new-text`
++//   -> UIConv notifyObservers `received-message`
++//   -> protocol prepareForDisplaying
++//   -> UIConv notifyObservers `new-text`
++//
++// The test walks the sending path, which covers both.
++let test_message_transformation = function() {
++  let conv = new Conversation();
++  conv.sendMsg = function(aMsg) {
++    this.writeMessage("user", aMsg, {outgoing: true});
++  };
++
++  let uiConv = new imConversations.UIConversation(conv);
++  let message = "Hello!";
++  let receivedMsg = false, newTxt = false;
++
++  uiConv.addObserver({
++    observe: function(aObject, aTopic, aMsg) {
++      switch(aTopic) {
++        case "sending-message":
++          ok(!newTxt);
++          ok(!receivedMsg);
++          ok(aObject instanceof imConversations.OutgoingMessage);
++          aObject.message = rot13(aObject.message);
++          break;
++        case "received-message":
++          ok(!newTxt);
++          ok(!receivedMsg);
++          ok(aObject.outgoing);
++          ok(aObject instanceof imConversations.imMessage);
++          equal(aObject.displayMessage, rot13(message));
++          aObject.displayMessage = rot13(aObject.displayMessage);
++          receivedMsg = true;
++          break;
++        case "new-text":
++          ok(!newTxt);
++          ok(receivedMsg);
++          ok(aObject.outgoing);
++          ok(aObject instanceof imConversations.imMessage);
++          equal(aObject.displayMessage, message);
++          newTxt = true;
++          break;
++      }
++    }
++  });
++  uiConv.sendMsg(message);
++
++  ok(newTxt);
++};
++
++// A test that cancels a message before it can be sent.
++let test_cancel_send_message = function() {
++    let conv = new Conversation();
++    conv.sendMsg = function(aMsg) {
++      ok(false);
++    };
++
++    let uiConv = new imConversations.UIConversation(conv);
++    uiConv.addObserver({
++      observe: function(aObject, aTopic, aMsg) {
++        if (aTopic === "sending-message") {
++          ok(aObject instanceof imConversations.OutgoingMessage);
++          aObject.cancelled = true;
++        }
++      }
++    });
++    uiConv.sendMsg("Hi!");
++};
++
++// A test that cancels a message before it gets displayed.
++let test_cancel_display_message = function() {
++    let conv = new Conversation();
++    conv.sendMsg = function(aMsg) {
++      this.writeMessage("user", aMsg, {outgoing: true});
++    };
++
++    let uiConv = new imConversations.UIConversation(conv);
++    uiConv.addObserver({
++      observe: function(aObject, aTopic, aMsg) {
++        switch(aTopic) {
++          case "received-message":
++            ok(aObject instanceof imConversations.imMessage);
++            aObject.cancelled = true;
++            break;
++          case "new-text":
++            ok(false);
++            break;
++        }
++      }
++    });
++    uiConv.sendMsg("Hi!");
++};
++
++// A test that ensures protocols get a chance to prepare a message before
++// sending and displaying.
++let test_prpl_message_prep = function() {
++    let conv = new Conversation();
++    conv.sendMsg = function(aMsg) {
++      this.writeMessage("user", aMsg, {outgoing: true});
++    };
++
++    let msg = "Hi!";
++    let prefix = "test> ";
++
++    let prepared = false;
++    conv.prepareForSending = function(aMsg) {
++      ok(aMsg instanceof imConversations.OutgoingMessage);
++      equal(aMsg.message, msg);
++      aMsg.message = prefix + aMsg.message;
++      prepared = true;
++    };
++
++    conv.prepareForDisplaying = function(aMsg) {
++      ok(aMsg instanceof imConversations.imMessage);
++      equal(aMsg.displayMessage, prefix + msg);
++      aMsg.displayMessage = aMsg.displayMessage.slice(prefix.length);
++    };
++
++    let receivedMsg = false;
++    let uiConv = new imConversations.UIConversation(conv);
++    uiConv.addObserver({
++      observe: function(aObject, aTopic, aMsg) {
++        if (aTopic === "new-text") {
++          equal(aObject.displayMessage, msg);
++          receivedMsg = true;
++        }
++      }
++    });
++    uiConv.sendMsg(msg);
++    ok(prepared);
++    ok(receivedMsg);
++};
++
++// A test that ensures protocols can split messages before they are sent.
++let test_split_message_before_sending = function() {
++    let msgCount = 0;
++    let prepared = false;
++
++    let conv = new Conversation();
++    conv.sendMsg = function(aMsg) {
++      ++msgCount;
++    };
++    conv.prepareForSending = function(aMsg) {
++      ok(aMsg instanceof imConversations.OutgoingMessage);
++      prepared = true;
++      return aMsg.message.split("\n");
++    };
++
++    let uiConv = new imConversations.UIConversation(conv);
++    uiConv.sendMsg("This is a looo\nooong message.");
++
++    ok(prepared);
++    equal(msgCount, 2);
++};
++
++function run_test() {
++  test_null_message();
++  test_message_transformation();
++  test_cancel_send_message();
++  test_cancel_display_message();
++  test_prpl_message_prep();
++  test_split_message_before_sending();
++  run_next_test();
++}
+diff --git a/chat/components/src/test/xpcshell.ini b/chat/components/src/test/xpcshell.ini
+--- a/chat/components/src/test/xpcshell.ini
++++ b/chat/components/src/test/xpcshell.ini
+@@ -3,9 +3,10 @@
+ ; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ [DEFAULT]
+ head = 
+ tail = 
+ 
+ [test_accounts.js]
+ [test_commands.js]
++[test_conversations.js]
+ [test_logger.js]



More information about the tor-commits mailing list