Last active
August 29, 2015 14:02
-
-
Save arlolra/c6ee91363f22e354498b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/chat/components/public/prplIConversation.idl b/chat/components/public/prplIConversation.idl | |
--- a/chat/components/public/prplIConversation.idl | |
+++ b/chat/components/public/prplIConversation.idl | |
@@ -6,16 +6,17 @@ | |
#include "nsISupports.idl" | |
#include "nsISimpleEnumerator.idl" | |
#include "nsIObserver.idl" | |
interface imIAccountBuddy; | |
interface imIAccount; | |
interface nsIURI; | |
interface nsIDOMDocument; | |
+interface prplIMessage; | |
/* | |
* This is the XPCOM purple conversation component, a proxy for PurpleConversation. | |
*/ | |
[scriptable, uuid(e40dc3e5-c9ff-457b-a6cc-655cce81042c)] | |
interface prplIConversation: nsISupports { | |
@@ -57,18 +58,29 @@ interface prplIConversation: nsISupports | |
/* When the conversation is closed from the UI. */ | |
void close(); | |
/* Method to add or remove an observer */ | |
void addObserver(in nsIObserver aObserver); | |
void removeObserver(in nsIObserver aObserver); | |
- /* Observers will be all receive new-text notifications. | |
- aSubject will contain the message (prplIMessage) */ | |
+ /* Observers will be notified of messaging events. | |
+ * aSubject will contain the message (prplIMessage) | |
+ * | |
+ * Fired notifications: | |
+ * new-text | |
+ * Tells the UI to display the message in the conversation. | |
+ * sending-message | |
+ * Where the message can be modified, or cancelled, by observers. | |
+ * receiving-message | |
+ * Observers can modify the message, or stop its processing. | |
+ */ | |
+ void notifyObservers(in prplIMessage aSubject, in string aTopic, | |
+ [optional] in wstring aData); | |
}; | |
[scriptable, uuid(0c072a80-103a-4992-b249-8e442b5f0d46)] | |
interface prplIConvIM: prplIConversation { | |
/* The buddy at the remote end of the conversation */ | |
readonly attribute imIAccountBuddy buddy; | |
diff --git a/chat/components/public/prplIMessage.idl b/chat/components/public/prplIMessage.idl | |
--- a/chat/components/public/prplIMessage.idl | |
+++ b/chat/components/public/prplIMessage.idl | |
@@ -21,17 +21,17 @@ interface prplIMessageAction: nsIRunnabl | |
[scriptable, uuid(d9f0ca7f-ee59-4657-a3dd-f458c204ca45)] | |
interface prplIMessage: nsISupports { | |
/* The uniqueness of the message id is only guaranteed across | |
messages of a conversation, not across all messages created | |
during the execution of the application. */ | |
readonly attribute unsigned long id; | |
readonly attribute AUTF8String who; | |
readonly attribute AUTF8String alias; | |
- readonly attribute AUTF8String originalMessage; | |
+ attribute AUTF8String originalMessage; | |
attribute AUTF8String message; | |
readonly attribute AUTF8String iconURL; | |
readonly attribute PRTime time; | |
readonly attribute prplIConversation conversation; | |
/* Holds the sender color for Chats. | |
Empty string by default, it is set by the conversation binding. */ | |
attribute AUTF8String color; | |
@@ -51,17 +51,17 @@ interface prplIMessage: nsISupports { | |
internal UI purposes | |
(e.g. for contact-aware | |
conversions). */ | |
/* PURPLE_MESSAGE_NICK = 0x0020, /**< Contains your nick. */ | |
readonly attribute boolean containsNick; | |
/* PURPLE_MESSAGE_NO_LOG = 0x0040, /**< Do not log. */ | |
readonly attribute boolean noLog; | |
/* PURPLE_MESSAGE_ERROR = 0x0200, /**< Error message. */ | |
- readonly attribute boolean error; | |
+ attribute boolean error; | |
/* PURPLE_MESSAGE_DELAYED = 0x0400, /**< Delayed message. */ | |
readonly attribute boolean delayed; | |
/* PURPLE_MESSAGE_RAW = 0x0800, /**< "Raw" message - don't | |
apply formatting */ | |
readonly attribute boolean noFormat; | |
/* PURPLE_MESSAGE_IMAGES = 0x1000, /**< Message contains images */ | |
readonly attribute boolean containsImages; | |
/* PURPLE_MESSAGE_NOTIFY = 0x2000, /**< Message is a notification */ | |
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 | |
@@ -16,16 +16,17 @@ XPCOMUtils.defineLazyGetter(this, "bundl | |
); | |
function UIConversation(aPrplConversation) | |
{ | |
this._prplConv = {}; | |
this.id = ++gLastUIConvId; | |
this._observers = []; | |
this._messages = []; | |
+ this._msgBuffer = []; | |
this.changeTargetTo(aPrplConversation); | |
let iface = Ci["prplIConv" + (aPrplConversation.isChat ? "Chat" : "IM")]; | |
this._interfaces = this._interfaces.concat(iface); | |
// XPConnect will create a wrapper around 'this' after here, | |
// so the list of exposed interfaces shouldn't change anymore. | |
this.updateContactObserver(); | |
Services.obs.notifyObservers(this, "new-ui-conversation", null); | |
} | |
@@ -261,16 +262,21 @@ UIConversation.prototype = { | |
}, | |
observeConv: function(aTargetId, aSubject, aTopic, aData) { | |
if (aTargetId != this._currentTargetId && | |
(aTopic == "new-text" || | |
(aTopic == "update-typing" && | |
this._prplConv[aTargetId].typingState == Ci.prplIConvIM.TYPING))) | |
this.target = this._prplConv[aTargetId]; | |
+ | |
+ // Process new texts and possibly cancel the message at this point. | |
+ if (aTopic == "new-text" && !this.processText(aSubject)) | |
+ return; | |
+ | |
this.notifyObservers(aSubject, aTopic, aData); | |
if (aTopic == "new-text") { | |
Services.obs.notifyObservers(aSubject, aTopic, aData); | |
if (aSubject.incoming && !aSubject.system && | |
(!this.isChat || aSubject.containsNick)) { | |
this.notifyObservers(aSubject, "new-directed-incoming-message", aData); | |
Services.obs.notifyObservers(aSubject, "new-directed-incoming-message", aData); | |
} | |
@@ -284,17 +290,81 @@ UIConversation.prototype = { | |
// prplIConversation | |
get isChat() this.target.isChat, | |
get account() this.target.account, | |
get name() this.target.name, | |
get normalizedName() this.target.normalizedName, | |
get title() this.target.title, | |
get startDate() this.target.startDate, | |
- sendMsg: function (aMsg) { this.target.sendMsg(aMsg); }, | |
+ | |
+ sendMsg: function (aMsg) { | |
+ // Create a new message with aMsg to satisfy the observer interface. | |
+ let nMsg = new Message(this.target.name, aMsg, { outgoing: true }); | |
+ | |
+ // Notify observers that now is the time to modify the message | |
+ // before sending. After modifications, | |
+ // "message" will contain the outgoing text | |
+ // "originalMessage" will be displayed to the user | |
+ this.target.notifyObservers(nMsg, "sending-message", null); | |
+ | |
+ // If an observer cancelled the message, abort here. | |
+ if (nMsg.error) | |
+ return; | |
+ | |
+ // Because of the libpurple context restriction, we're forced | |
+ // to buffer messages here. When "new-text" is fired, we'll | |
+ // grab this message to display the intended content. | |
+ this.bufferMsg(this.target, nMsg); | |
+ | |
+ // Send just the outgoing text. | |
+ this.target.sendMsg(nMsg.message); | |
+ }, | |
+ | |
+ bufferMsg: function(aConv, aMsg) { | |
+ this._msgBuffer.push({ conv: aConv, msg: aMsg }); | |
+ }, | |
+ | |
+ // If we don't find the message, it was injected directly | |
+ // and was intended to be ignored. | |
+ pluckMsg: function(aConv, aMsg) { | |
+ let mb = this._msgBuffer; | |
+ return mb.some(function(a, i) { | |
+ if (a.conv === aConv && a.msg.message === aMsg.message) { | |
+ // This is likely the message we stored. | |
+ // Replace the text to be displayed with its contents. | |
+ aMsg.originalMessage = a.msg.originalMessage; | |
+ // And remove it from the buffer. | |
+ mb.splice(i, 1); | |
+ // Indicate the message was located. | |
+ return true; | |
+ } | |
+ }); | |
+ }, | |
+ | |
+ processText: function(aMsg) { | |
+ if (!aMsg.system) { | |
+ if (aMsg.outgoing) { | |
+ // If the message isn't located, suppress it. | |
+ return this.pluckMsg(this.target, aMsg); | |
+ } else if (aMsg.incoming) { | |
+ // Notify observers that now is the time to modify the incoming | |
+ // message. After modifications, | |
+ // "message" will still contain the incoming text | |
+ // "originalMessage" will be displayed to the user | |
+ this.target.notifyObservers(aMsg, "receiving-message", null); | |
+ | |
+ // If an observer cancelled the message, abort here. | |
+ if (aMsg.error) | |
+ return false; | |
+ } | |
+ } | |
+ return true; | |
+ }, | |
+ | |
unInit: function() { | |
for each (let conv in this._prplConv) | |
gConversationsService.forgetConversation(conv); | |
if (this._observedContact) { | |
this._observedContact.removeObserver(this); | |
delete this._observedContact; | |
} | |
this._prplConv = {}; // Prevent .close from failing. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment