Bug 1599380 - WebExtensions API event for when new mail arrives. r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Thu, 05 Mar 2020 12:21:26 +1300
changeset 37520 2679ad2daf985416a6c7956b59dfdc15d6b11f68
parent 37519 291626c26cf9196a59433a9d4a9293765c3aa00e
child 37521 f87a75425b97da24ec0314009e7d18e08dbfffc1
push id2566
push userclokep@gmail.com
push dateMon, 09 Mar 2020 19:20:31 +0000
treeherdercomm-beta@a352facfa0a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1599380
Bug 1599380 - WebExtensions API event for when new mail arrives. r=mkmelin
mail/components/extensions/parent/ext-messages.js
mail/components/extensions/schemas/messages.json
mail/components/extensions/test/xpcshell/head.js
mail/components/extensions/test/xpcshell/test_ext_messages_onNewMailReceived.js
mail/components/extensions/test/xpcshell/xpcshell.ini
--- a/mail/components/extensions/parent/ext-messages.js
+++ b/mail/components/extensions/parent/ext-messages.js
@@ -56,16 +56,53 @@ function convertMessagePart(part) {
     }
   }
   if ("parts" in part && Array.isArray(part.parts) && part.parts.length > 0) {
     partObject.parts = part.parts.map(convertMessagePart);
   }
   return partObject;
 }
 
+/**
+ * Listens to the folder notification service for new messages, which are
+ * passed to the onNewMailReceived event.
+ *
+ * @implements {nsIMsgFolderListener}
+ */
+let newMailEventTracker = new (class extends EventEmitter {
+  constructor() {
+    super();
+    this.listenerCount = 0;
+  }
+  on(event, listener) {
+    super.on(event, listener);
+
+    if (++this.listenerCount == 1) {
+      MailServices.mfn.addListener(this, MailServices.mfn.msgsClassified);
+    }
+  }
+  off(event, listener) {
+    super.off(event, listener);
+
+    if (--this.listenerCount == 0) {
+      MailServices.mfn.removeListener(this);
+    }
+  }
+
+  msgsClassified(messages, junkProcessed, traitProcessed) {
+    if (messages.length > 0) {
+      this.emit(
+        "new-mail-received",
+        messages.queryElementAt(0, Ci.nsIMsgDBHdr).folder,
+        messages.enumerate()
+      );
+    }
+  }
+})();
+
 this.messages = class extends ExtensionAPI {
   getAPI(context) {
     function collectMessagesInFolders(messageIds) {
       let folderMap = new DefaultMap(() => new Set());
 
       for (let id of messageIds) {
         let msgHdr = messageTracker.getMessage(id);
         if (!msgHdr) {
@@ -126,16 +163,33 @@ this.messages = class extends ExtensionA
           throw new ExtensionError(`Unexpected error moving messages: ${ex}`);
         }
         throw new ExtensionError(`Unexpected error copying messages: ${ex}`);
       }
     }
 
     return {
       messages: {
+        onNewMailReceived: new EventManager({
+          context,
+          name: "messageDisplay.onNewMailReceived",
+          register: fire => {
+            let listener = (event, folder, newMessages) => {
+              fire.async(
+                convertFolder(folder),
+                messageListTracker.startList(newMessages, context.extension)
+              );
+            };
+
+            newMailEventTracker.on("new-mail-received", listener);
+            return () => {
+              newMailEventTracker.off("new-mail-received", listener);
+            };
+          },
+        }).api(),
         async list({ accountId, path }) {
           let uri = folderPathToURI(accountId, path);
           let folder = MailServices.folderLookup.getFolderForURL(uri);
 
           return messageListTracker.startList(
             folder.messages,
             context.extension
           );
--- a/mail/components/extensions/schemas/messages.json
+++ b/mail/components/extensions/schemas/messages.json
@@ -42,16 +42,36 @@
             "enum": [
               "all",
               "any"
             ]
           }
         }
       }
     ],
+    "events": [
+      {
+        "name": "onNewMailReceived",
+        "type": "function",
+        "description": "Fired when a new message is received, and has been through junk classification and message filters.",
+        "permissions": [
+          "accountsRead"
+        ],
+        "parameters": [
+          {
+            "name": "folder",
+            "$ref": "folders.MailFolder"
+          },
+          {
+            "name": "messages",
+            "$ref": "messages.MessageList"
+          }
+        ]
+      }
+    ],
     "functions": [
       {
         "name": "list",
         "type": "function",
         "description": "Gets all messages in a folder.",
         "async": true,
         "parameters": [
           {
--- a/mail/components/extensions/test/xpcshell/head.js
+++ b/mail/components/extensions/test/xpcshell/head.js
@@ -58,17 +58,20 @@ function addIdentity(account, email = "x
   }
   info(`Created identity ${identity.toString()}`);
 }
 
 function createMessages(folder, count) {
   const { MessageGenerator } = ChromeUtils.import(
     "resource://testing-common/mailnews/MessageGenerator.jsm"
   );
-  let messages = new MessageGenerator().makeMessages({
+  if (!createMessages.messageGenerator) {
+    createMessages.messageGenerator = new MessageGenerator();
+  }
+  let messages = createMessages.messageGenerator.makeMessages({
     count,
     age_incr: { days: 2 },
   });
   let messageStrings = messages.map(message => message.toMboxString());
   folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
   folder.addMessageBatch(messageStrings);
   folder.callFilterPlugins(null);
 }
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/test/xpcshell/test_ext_messages_onNewMailReceived.js
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { ExtensionTestUtils } = ChromeUtils.import(
+  "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+ExtensionTestUtils.init(this);
+
+let localAccount = createAccount();
+let rootFolder = localAccount.incomingServer.rootFolder;
+rootFolder.createSubfolder("test1", null);
+let inbox = rootFolder.getChildNamed("test1");
+
+add_task(async function() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background: async () => {
+      browser.messages.onNewMailReceived.addListener((folder, messageList) => {
+        browser.test.sendMessage("newMessages", messageList.messages);
+      });
+    },
+    manifest: { permissions: ["accountsRead", "messagesRead"] },
+  });
+
+  await extension.startup();
+
+  createMessages(inbox, 1);
+  let inboxMessages = [...inbox.messages];
+
+  let newMessages = await extension.awaitMessage("newMessages");
+  equal(newMessages.length, 1);
+  equal(newMessages[0].subject, inboxMessages[0].subject);
+
+  createMessages(inbox, 2);
+  inboxMessages = [...inbox.messages];
+  newMessages = await extension.awaitMessage("newMessages");
+  equal(newMessages.length, 2);
+  equal(newMessages[0].subject, inboxMessages[1].subject);
+  equal(newMessages[1].subject, inboxMessages[2].subject);
+
+  await extension.unload();
+});
--- a/mail/components/extensions/test/xpcshell/xpcshell.ini
+++ b/mail/components/extensions/test/xpcshell/xpcshell.ini
@@ -7,9 +7,10 @@ tags = webextensions
 tags = addrbook
 [test_ext_alias.js]
 [test_ext_cloudFile.js]
 support-files = data/cloudFile1.txt data/cloudFile2.txt
 [test_ext_experiments.js]
 [test_ext_folders.js]
 [test_ext_messages.js]
 [test_ext_messages_get.js]
+[test_ext_messages_onNewMailReceived.js]
 [test_ext_messages_query.js]