Bug 1138689 - /me and /action should fire a sending-message notification before actually sending the action. r=clokep
--- a/AUTHORS
+++ b/AUTHORS
@@ -25,16 +25,17 @@ alta88 <alta88@gmail.com>
Andreas Nilsson <nisses.mail@home.se>
Andrew Sutherland <asutherland@asutherland.org>
Andrew Wooldridge <andreww@netscape.com>
Andrey Terentyev <andreycpp@gmail.com>
Anirvana Mishra <anidps@gmail.com>
Annie Sullivan <annie.sullivan@gmail.com>
Archaeopteryx <archaeopteryx@coole-files.de>
ArentJan Banck <ajbanck@planet.nl>
+Arlo Breault <arlolra@gmail.com>
Arthur Wiebe <artooro@gmail.com>
Asaf Romano <mano@mozilla.com>
Atul Varma <atul@mozilla.com>
ause <ause@sun.com>
Axel Zechner <axel.zechner@googlemail.com>
Baki Bon <bakibon@yahoo.com>
Ben Bucksch <mozilla@bucksch.org>
Benedikt Pfeifer <benediktp@ymail.com>
--- a/chat/components/public/imIConversationsService.idl
+++ b/chat/components/public/imIConversationsService.idl
@@ -73,20 +73,22 @@ interface imIConversationsService: nsISu
// 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 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)]
+[scriptable, uuid(f88535b1-0b99-433b-a6de-c1a4bf8b43ea)]
interface imIOutgoingMessage: nsISupports {
attribute AUTF8String message;
attribute boolean cancelled;
+ // Outgoing message is an action command.
+ readonly attribute boolean action;
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 of `received-message`
// (again, typically add-ons), which have the opportunity to swap or cancel
// the message.
--- a/chat/components/src/imConversations.js
+++ b/chat/components/src/imConversations.js
@@ -16,17 +16,18 @@ XPCOMUtils.defineLazyGetter(this, "bundl
);
function OutgoingMessage(aMsg, aConversation) {
this.message = aMsg;
this.conversation = aConversation;
}
OutgoingMessage.prototype = {
__proto__: ClassInfo("imIOutgoingMessage", "Outgoing Message"),
- cancelled: false
+ cancelled: false,
+ action: false
};
function imMessage(aPrplMessage) {
this.prplMessage = aPrplMessage;
}
imMessage.prototype = {
__proto__: ClassInfo(["imIMessage", "prplIMessage"], "IM Message"),
cancelled: false,
--- a/chat/protocols/irc/ircCommands.jsm
+++ b/chat/protocols/irc/ircCommands.jsm
@@ -15,16 +15,26 @@ Cu.import("resource:///modules/ircUtils.
function getConv(aConv) aConv.wrappedJSObject;
// Shortcut to get the JavaScript account object.
function getAccount(aConv) getConv(aConv)._account;
// Trim leading and trailing spaces and split a string by any type of space.
function splitInput(aString) aString.trim().split(/\s+/);
+function OutgoingMessage(aMsg, aConversation, aAction) {
+ this.message = aMsg;
+ this.conversation = aConversation;
+ this.action = !!aAction;
+}
+OutgoingMessage.prototype = {
+ __proto__: ClassInfo("imIOutgoingMessage", "Outgoing Message"),
+ cancelled: false
+};
+
// Kick a user from a channel
// aMsg is <user> [comment]
function kickCommand(aMsg, aConv) {
if (!aMsg.length)
return false;
let params = [aConv.name];
let offset = aMsg.indexOf(" ");
@@ -60,22 +70,17 @@ function messageCommand(aMsg, aConv, aRe
let conv = getAccount(aConv).getConversation(nickname);
if (aReturnedConv)
aReturnedConv.value = conv;
if (!message.length)
return true;
// Give add-ons an opportunity to tweak or cancel the message.
- let om = {
- __proto__: ClassInfo("imIOutgoingMessage", "Outgoing Message"),
- message: message,
- conversation: conv,
- cancelled: false
- };
+ let om = new OutgoingMessage(message, conv);
conv.notifyObservers(om, "sending-message");
// If a NOTICE is cancelled and resent, it will end up being sent as PRIVMSG.
if (om.cancelled)
return true;
return privateMessage(aConv, om.message, nickname, aReturnedConv, aIsNotice);
}
@@ -91,25 +96,32 @@ function setMode(aNickname, aConv, aMode
}
function actionCommand(aMsg, aConv) {
// Don't try to send an empty action.
if (!aMsg || !aMsg.trim().length)
return false;
let conv = getConv(aConv);
+
+ // Give add-ons an opportunity to tweak or cancel the action.
+ let om = new OutgoingMessage(aMsg, aConv, true);
+ conv.notifyObservers(om, "sending-message");
+ if (om.cancelled)
+ return true;
+
let account = getAccount(aConv);
- if (!ctcpCommand(aConv, aConv.name, "ACTION", aMsg)) {
+ if (!ctcpCommand(aConv, aConv.name, "ACTION", om.message)) {
conv.writeMessage(account._currentServerName, _("error.sendMessageFailed"),
{error: true, system: true});
return true;
}
// Show the action on our conversation.
- conv.writeMessage(account._nickname, "/me " + aMsg, {outgoing: true});
+ conv.writeMessage(account._nickname, "/me " + om.message, {outgoing: true});
return true;
}
// This will open the conversation, and send and display the text.
// aReturnedConv is optional and returns the resulting conversation.
// aIsNotice is optional and sends a NOTICE instead of a PRIVMSG.
function privateMessage(aConv, aMsg, aNickname, aReturnedConv, aIsNotice) {
if (!aMsg.length)