Bug 1681153 - add support for _execute_compose_action and _execute_message_display_action commands in commands API. r=darktrojan
authorJohn Bieling <john@thunderbird.net>
Thu, 28 Jan 2021 12:49:40 +0200
changeset 31540 137d5a4c564ac9846256642d1d569525f3106d64
parent 31539 ea8fc7b7757e00b147cb2bdee3dc2ab62184f916
child 31541 d3e7a67ff19198721ad35c8ab2f8914da54a91b6
push id18400
push usermkmelin@iki.fi
push dateThu, 28 Jan 2021 10:50:42 +0000
treeherdercomm-central@90c7a8c7c110 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdarktrojan
bugs1681153
Bug 1681153 - add support for _execute_compose_action and _execute_message_display_action commands in commands API. r=darktrojan
mail/components/extensions/MailExtensionShortcuts.jsm
mail/components/extensions/jar.mn
mail/components/extensions/moz.build
mail/components/extensions/parent/ext-commands.js
mail/components/extensions/parent/ext-composeAction.js
mail/components/extensions/test/browser/browser.ini
mail/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
mail/components/extensions/test/browser/browser_ext_commands_execute_compose_action.js
mail/components/extensions/test/browser/browser_ext_commands_execute_message_display_action.js
mail/components/extensions/test/browser/browser_ext_composeAction.js
mail/components/extensions/test/browser/browser_ext_messageDisplayAction.js
mail/components/extensions/test/browser/head.js
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/MailExtensionShortcuts.jsm
@@ -0,0 +1,85 @@
+/* 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/. */
+"use strict";
+
+/* exported MailExtensionShortcuts */
+const EXPORTED_SYMBOLS = ["MailExtensionShortcuts"];
+
+const { XPCOMUtils } = ChromeUtils.import(
+  "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+  this,
+  "ExtensionParent",
+  "resource://gre/modules/ExtensionParent.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+  this,
+  "ExtensionShortcuts",
+  "resource://gre/modules/ExtensionShortcuts.jsm"
+);
+
+XPCOMUtils.defineLazyGetter(this, "browserActionFor", () => {
+  return ExtensionParent.apiManager.global.browserActionFor;
+});
+
+XPCOMUtils.defineLazyGetter(this, "composeActionFor", () => {
+  return ExtensionParent.apiManager.global.composeActionFor;
+});
+
+XPCOMUtils.defineLazyGetter(this, "messageDisplayActionFor", () => {
+  return ExtensionParent.apiManager.global.messageDisplayActionFor;
+});
+
+const EXECUTE_BROWSER_ACTION = "_execute_browser_action";
+const EXECUTE_MSG_DISPLAY_ACTION = "_execute_message_display_action";
+const EXECUTE_COMPOSE_ACTION = "_execute_compose_action";
+
+class MailExtensionShortcuts extends ExtensionShortcuts {
+  /**
+   * Builds a XUL Key element and attaches an onCommand listener which
+   * emits a command event with the provided name when fired.
+   *
+   * @param {Document} doc The XUL document.
+   * @param {string} name The name of the command.
+   * @param {string} shortcut The shortcut provided in the manifest.
+   * @see https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/key
+   *
+   * @returns {Document} The newly created Key element.
+   */
+  buildKey(doc, name, shortcut) {
+    let keyElement = this.buildKeyFromShortcut(doc, name, shortcut);
+
+    // We need to have the attribute "oncommand" for the "command" listener to fire,
+    // and it is currently ignored when set to the empty string.
+    keyElement.setAttribute("oncommand", "//");
+
+    /* eslint-disable mozilla/balanced-listeners */
+    // We remove all references to the key elements when the extension is shutdown,
+    // therefore the listeners for these elements will be garbage collected.
+    keyElement.addEventListener("command", event => {
+      let action;
+      if (name == EXECUTE_BROWSER_ACTION) {
+        action = browserActionFor(this.extension);
+      } else if (name == EXECUTE_COMPOSE_ACTION) {
+        action = composeActionFor(this.extension);
+      } else if (name == EXECUTE_MSG_DISPLAY_ACTION) {
+        action = messageDisplayActionFor(this.extension);
+      } else {
+        this.extension.tabManager.addActiveTabPermission();
+        this.onCommand(name);
+        return;
+      }
+      if (action) {
+        let win = event.target.ownerGlobal;
+        action.triggerAction(win);
+      }
+    });
+    /* eslint-enable mozilla/balanced-listeners */
+
+    return keyElement;
+  }
+}
--- a/mail/components/extensions/jar.mn
+++ b/mail/components/extensions/jar.mn
@@ -16,17 +16,17 @@ messenger.jar:
     content/messenger/child/ext-menus.js           (child/ext-menus.js)
     content/messenger/child/ext-tabs.js            (child/ext-tabs.js)
 
     content/messenger/parent/ext-accounts.js       (parent/ext-accounts.js)
     content/messenger/parent/ext-addressBook.js    (parent/ext-addressBook.js)
     content/messenger/parent/ext-browserAction.js  (parent/ext-browserAction.js)
     content/messenger/parent/ext-chrome-settings-overrides.js      (parent/ext-chrome-settings-overrides.js)
     content/messenger/parent/ext-cloudFile.js      (parent/ext-cloudFile.js)
-    content/messenger/parent/ext-commands.js       (../../../../browser/components/extensions/parent/ext-commands.js)
+    content/messenger/parent/ext-commands.js       (parent/ext-commands.js)
     content/messenger/parent/ext-compose.js        (parent/ext-compose.js)
     content/messenger/parent/ext-composeAction.js  (parent/ext-composeAction.js)
     content/messenger/parent/ext-extensionScripts.js (parent/ext-extensionScripts.js)
     content/messenger/parent/ext-folders.js        (parent/ext-folders.js)
     content/messenger/parent/ext-mail.js           (parent/ext-mail.js)
     content/messenger/parent/ext-mailTabs.js       (parent/ext-mailTabs.js)
     content/messenger/parent/ext-menus.js          (parent/ext-menus.js)
     content/messenger/parent/ext-messageDisplay.js (parent/ext-messageDisplay.js)
--- a/mail/components/extensions/moz.build
+++ b/mail/components/extensions/moz.build
@@ -5,16 +5,17 @@
 EXTRA_COMPONENTS += [
     "extensions-mail.manifest",
 ]
 
 EXTRA_JS_MODULES += [
     "ExtensionBrowsingData.jsm",
     "ExtensionPopups.jsm",
     "ExtensionToolbarButtons.jsm",
+    "MailExtensionShortcuts.jsm",
 ]
 
 JAR_MANIFESTS += ["jar.mn"]
 
 BROWSER_CHROME_MANIFESTS += ["test/browser/browser.ini"]
 XPCSHELL_TESTS_MANIFESTS += [
     "test/xpcshell/xpcshell-imap.ini",
     "test/xpcshell/xpcshell-local.ini",
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/parent/ext-commands.js
@@ -0,0 +1,57 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+/* 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/. */
+
+"use strict";
+
+ChromeUtils.defineModuleGetter(
+  this,
+  "MailExtensionShortcuts",
+  "resource:///modules/MailExtensionShortcuts.jsm"
+);
+
+this.commands = class extends ExtensionAPI {
+  static onUninstall(extensionId) {
+    return MailExtensionShortcuts.removeCommandsFromStorage(extensionId);
+  }
+
+  async onManifestEntry(entryName) {
+    let shortcuts = new MailExtensionShortcuts({
+      extension: this.extension,
+      onCommand: name => this.emit("command", name),
+    });
+    this.extension.shortcuts = shortcuts;
+    await shortcuts.loadCommands();
+    await shortcuts.register();
+  }
+
+  onShutdown() {
+    this.extension.shortcuts.unregister();
+  }
+
+  getAPI(context) {
+    return {
+      commands: {
+        getAll: () => this.extension.shortcuts.allCommands(),
+        update: args => this.extension.shortcuts.updateCommand(args),
+        reset: name => this.extension.shortcuts.resetCommand(name),
+        onCommand: new EventManager({
+          context,
+          name: "commands.onCommand",
+          inputHandling: true,
+          register: fire => {
+            let listener = (eventName, commandName) => {
+              fire.async(commandName);
+            };
+            this.on("command", listener);
+            return () => {
+              this.off("command", listener);
+            };
+          },
+        }).api(),
+      },
+    };
+  }
+};
--- a/mail/components/extensions/parent/ext-composeAction.js
+++ b/mail/components/extensions/parent/ext-composeAction.js
@@ -7,17 +7,33 @@
 "use strict";
 
 ChromeUtils.defineModuleGetter(
   this,
   "ToolbarButtonAPI",
   "resource:///modules/ExtensionToolbarButtons.jsm"
 );
 
+const composeActionMap = new WeakMap();
+
 this.composeAction = class extends ToolbarButtonAPI {
+  static for(extension) {
+    return composeActionMap.get(extension);
+  }
+
+  async onManifestEntry(entryName) {
+    await super.onManifestEntry(entryName);
+    composeActionMap.set(this.extension, this);
+  }
+
+  close() {
+    super.close();
+    composeActionMap.delete(this.extension);
+  }
+
   constructor(extension) {
     super(extension, global);
     this.manifest_name = "compose_action";
     this.manifestName = "composeAction";
     this.windowURLs = [
       "chrome://messenger/content/messengercompose/messengercompose.xhtml",
     ];
 
@@ -65,8 +81,10 @@ this.composeAction = class extends Toolb
         windowURL,
         "composeToolbar2",
         "currentset",
         currentSet.join(",")
       );
     }
   }
 };
+
+global.composeActionFor = this.composeAction.for;
--- a/mail/components/extensions/test/browser/browser.ini
+++ b/mail/components/extensions/test/browser/browser.ini
@@ -13,16 +13,18 @@ subsuite = thunderbird
 support-files = ../xpcshell/data/utils.js
 tags = webextensions
 
 [browser_ext_addressBooksUI.js]
 tags = addrbook
 [browser_ext_browserAction.js]
 [browser_ext_browserAction_properties.js]
 [browser_ext_commands_execute_browser_action.js]
+[browser_ext_commands_execute_compose_action.js]
+[browser_ext_commands_execute_message_display_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_commands_update.js]
 [browser_ext_compose_attachments.js]
 [browser_ext_compose_begin.js]
 [browser_ext_compose_details.js]
 [browser_ext_compose_onBeforeSend.js]
 [browser_ext_composeAction.js]
--- a/mail/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
+++ b/mail/components/extensions/test/browser/browser_ext_commands_execute_browser_action.js
@@ -28,19 +28,21 @@ async function testExecuteBrowserActionW
     extensionOptions.files = {
       "popup.html": `
         <!DOCTYPE html>
         <html>
           <head>
             <meta charset="utf-8">
             <script src="popup.js"></script>
           </head>
+          <body>
+            Popup
+          </body>
         </html>
       `,
-
       "popup.js": function() {
         browser.runtime.sendMessage("from-browser-action-popup");
       },
     };
   }
 
   extensionOptions.background = () => {
     browser.test.onMessage.addListener((message, withPopup) => {
@@ -78,16 +80,17 @@ async function testExecuteBrowserActionW
   extension.onMessage("send-keys", () => {
     EventUtils.synthesizeKey("j", { altKey: true, shiftKey: true });
   });
 
   await extension.startup();
 
   await SimpleTest.promiseFocus(window);
 
+  // trigger setup of listeners in background and the send-keys msg
   extension.sendMessage("withPopup", options.withPopup);
 
   if (options.withPopup) {
     await extension.awaitFinish("execute-browser-action-popup-opened");
 
     if (!getBrowserActionPopup(extension)) {
       await awaitExtensionPanel(extension);
     }
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/test/browser/browser_ext_commands_execute_compose_action.js
@@ -0,0 +1,141 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let gAccount;
+
+async function testExecuteComposeActionWithOptions(options = {}) {
+  info(
+    `--> Running test commands_execute_compose_action with the following options: ${JSON.stringify(
+      options
+    )}`
+  );
+
+  let extensionOptions = {};
+  extensionOptions.manifest = {
+    permissions: ["accountsRead"],
+    commands: {
+      _execute_compose_action: {
+        suggested_key: {
+          default: "Alt+Shift+J",
+        },
+      },
+    },
+    compose_action: {
+      browser_style: true,
+    },
+  };
+
+  if (options.withFormatToolbar) {
+    extensionOptions.manifest.compose_action.default_area = "formattoolbar";
+  }
+
+  if (options.withPopup) {
+    extensionOptions.manifest.compose_action.default_popup = "popup.html";
+
+    extensionOptions.files = {
+      "popup.html": `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+            <script src="popup.js"></script>
+          </head>
+          <body>
+            Popup
+          </body>
+      </html>
+      `,
+      "popup.js": function() {
+        browser.test.log("sending from-compose-action-popup");
+        browser.runtime.sendMessage("from-compose-action-popup");
+      },
+    };
+  }
+
+  extensionOptions.background = async () => {
+    let accounts = await browser.accounts.list();
+    browser.test.assertEq(1, accounts.length, "number of accounts");
+
+    browser.test.onMessage.addListener((message, withPopup) => {
+      browser.commands.onCommand.addListener(commandName => {
+        if (commandName == "_execute_compose_action") {
+          browser.test.fail(
+            "The onCommand listener should never fire for _execute_compose_action."
+          );
+        }
+      });
+
+      browser.composeAction.onClicked.addListener(() => {
+        if (withPopup) {
+          browser.test.fail(
+            "The onClick listener should never fire if the composeAction has a popup."
+          );
+          browser.test.notifyFail("execute-compose-action-on-clicked-fired");
+        } else {
+          browser.test.notifyPass("execute-compose-action-on-clicked-fired");
+        }
+      });
+
+      browser.runtime.onMessage.addListener(msg => {
+        if (msg == "from-compose-action-popup") {
+          browser.test.notifyPass("execute-compose-action-popup-opened");
+        }
+      });
+
+      browser.test.log("Sending send-keys");
+      browser.test.sendMessage("send-keys");
+    });
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionOptions);
+  await extension.startup();
+
+  let composeWindow = await openComposeWindow(gAccount);
+  await focusWindow(composeWindow);
+
+  // trigger setup of listeners in background and the send-keys msg
+  extension.sendMessage("withPopup", options.withPopup);
+
+  await extension.awaitMessage("send-keys");
+  info("Simulating ALT+SHIFT+J");
+  EventUtils.synthesizeKey(
+    "j",
+    { altKey: true, shiftKey: true },
+    composeWindow
+  );
+
+  if (options.withPopup) {
+    await extension.awaitFinish("execute-compose-action-popup-opened");
+
+    let popup = composeWindow.document.getElementById(
+      makeWidgetId(extension.id) + "-panel"
+    );
+    let hidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+    popup.hidePopup();
+    await hidden;
+  } else {
+    await extension.awaitFinish("execute-compose-action-on-clicked-fired");
+  }
+  composeWindow.close();
+  await extension.unload();
+}
+
+add_task(async function prepare_test() {
+  gAccount = createAccount();
+  addIdentity(gAccount);
+});
+
+let popupJobs = [true, false];
+let formatToolbarJobs = [true, false];
+
+for (let popupJob of popupJobs) {
+  for (let formatToolbarJob of formatToolbarJobs) {
+    add_task(async () => {
+      await testExecuteComposeActionWithOptions({
+        withPopup: popupJob,
+        withFormatToolbar: formatToolbarJob,
+      });
+    });
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/test/browser/browser_ext_commands_execute_message_display_action.js
@@ -0,0 +1,166 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+let gMessages;
+
+async function testExecuteMessageDisplayActionWithOptions(msg, options = {}) {
+  info(
+    `--> Running test commands_execute_message_display_action with the following options: ${JSON.stringify(
+      options
+    )}`
+  );
+
+  let extensionOptions = {};
+  extensionOptions.manifest = {
+    commands: {
+      _execute_message_display_action: {
+        suggested_key: {
+          default: "Alt+Shift+J",
+        },
+      },
+    },
+    message_display_action: {
+      browser_style: true,
+    },
+  };
+
+  if (options.withPopup) {
+    extensionOptions.manifest.message_display_action.default_popup =
+      "popup.html";
+
+    extensionOptions.files = {
+      "popup.html": `
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+            <script src="popup.js"></script>
+          </head>
+          <body>
+            Popup
+          </body>
+        </html>
+      `,
+      "popup.js": function() {
+        browser.test.log("sending from-message-display-action-popup");
+        browser.runtime.sendMessage("from-message-display-action-popup");
+      },
+    };
+  }
+
+  extensionOptions.background = () => {
+    browser.test.onMessage.addListener((message, withPopup) => {
+      browser.commands.onCommand.addListener(commandName => {
+        if (commandName == "_execute_message_display_action") {
+          browser.test.fail(
+            "The onCommand listener should never fire for _execute_message_display_action."
+          );
+        }
+      });
+
+      browser.messageDisplayAction.onClicked.addListener(() => {
+        if (withPopup) {
+          browser.test.fail(
+            "The onClick listener should never fire if the messageDisplayAction has a popup."
+          );
+          browser.test.notifyFail(
+            "execute-message-display-action-on-clicked-fired"
+          );
+        } else {
+          browser.test.notifyPass(
+            "execute-message-display-action-on-clicked-fired"
+          );
+        }
+      });
+
+      browser.runtime.onMessage.addListener(msg => {
+        if (msg == "from-message-display-action-popup") {
+          browser.test.notifyPass(
+            "execute-message-display-action-popup-opened"
+          );
+        }
+      });
+
+      browser.test.log("Sending send-keys");
+      browser.test.sendMessage("send-keys");
+    });
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionOptions);
+  await extension.startup();
+  await promiseAnimationFrame();
+  let messageWindow = window;
+
+  switch (options.displayType) {
+    case "tab":
+      window.MsgOpenSelectedMessages();
+      break;
+    case "window":
+      messageWindow = await openNewWindowForMessage(msg);
+      break;
+  }
+
+  // trigger setup of listeners in background and the send-keys msg
+  extension.sendMessage("withPopup", options.withPopup);
+
+  await extension.awaitMessage("send-keys");
+  info("Simulating ALT+SHIFT+J");
+  EventUtils.synthesizeKey(
+    "j",
+    { altKey: true, shiftKey: true },
+    messageWindow
+  );
+
+  if (options.withPopup) {
+    await extension.awaitFinish("execute-message-display-action-popup-opened");
+
+    let popup = messageWindow.document.getElementById(
+      makeWidgetId(extension.id) + "-panel"
+    );
+    let hidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+    popup.hidePopup();
+    await hidden;
+  } else {
+    await extension.awaitFinish(
+      "execute-message-display-action-on-clicked-fired"
+    );
+  }
+
+  switch (options.displayType) {
+    case "tab":
+      document.getElementById("tabmail").closeTab();
+      break;
+    case "window":
+      messageWindow.close();
+      break;
+  }
+
+  await extension.unload();
+}
+
+add_task(async function prepare_test() {
+  let account = createAccount();
+  let rootFolder = account.incomingServer.rootFolder;
+  let subFolders = [...rootFolder.subFolders];
+  createMessages(subFolders[0], 10);
+  gMessages = [...subFolders[0].messages];
+
+  window.gFolderTreeView.selectFolder(subFolders[0]);
+  window.gFolderDisplay.selectViewIndex(0);
+  await BrowserTestUtils.browserLoaded(window.getMessagePaneBrowser());
+});
+
+let popupJobs = [true, false];
+let displayJobs = ["3pane", "tab", "window"];
+
+for (let popupJob of popupJobs) {
+  for (let displayJob of displayJobs) {
+    add_task(async () => {
+      await testExecuteMessageDisplayActionWithOptions(gMessages[1], {
+        withPopup: popupJob,
+        displayType: displayJob,
+      });
+    });
+  }
+}
--- a/mail/components/extensions/test/browser/browser_ext_composeAction.js
+++ b/mail/components/extensions/test/browser/browser_ext_composeAction.js
@@ -1,54 +1,20 @@
 /* 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/. */
 
-let gAccount;
-
-async function openComposeWindow() {
-  let params = Cc[
-    "@mozilla.org/messengercompose/composeparams;1"
-  ].createInstance(Ci.nsIMsgComposeParams);
-  let composeFields = Cc[
-    "@mozilla.org/messengercompose/composefields;1"
-  ].createInstance(Ci.nsIMsgCompFields);
-
-  params.identity = gAccount.defaultIdentity;
-  params.composeFields = composeFields;
-
-  await new Promise(resolve => {
-    let observer = {
-      observe(subject, topic, data) {
-        Services.ww.unregisterNotification(observer);
-        subject.addEventListener(
-          "load",
-          () => {
-            promiseAnimationFrame(subject).then(() => {
-              subject.setTimeout(resolve);
-            });
-          },
-          { once: true }
-        );
-      },
-    };
-    Services.ww.registerNotification(observer);
-    MailServices.compose.OpenComposeWindowWithParams(null, params);
-  });
-  return Services.wm.getMostRecentWindow("msgcompose");
-}
-
-async function test_it(extensionDetails, toolbarId) {
+async function test_it(account, extensionDetails, toolbarId) {
   let extension = ExtensionTestUtils.loadExtension(extensionDetails);
   let buttonId = "compose_action_mochi_test-composeAction-toolbarbutton";
 
   await extension.startup();
   await extension.awaitMessage();
 
-  let composeWindow = await openComposeWindow();
+  let composeWindow = await openComposeWindow(account);
   let composeDocument = composeWindow.document;
 
   await focusWindow(composeWindow);
 
   try {
     let toolbar = composeDocument.getElementById(toolbarId);
 
     let button = composeDocument.getElementById(buttonId);
@@ -112,18 +78,18 @@ async function test_it(extensionDetails,
         "Button removed from toolbar current set persistence"
       );
     }
     composeWindow.close();
   }
 }
 
 add_task(async function setup() {
-  gAccount = createAccount();
-  addIdentity(gAccount);
+  let account = createAccount();
+  addIdentity(account);
 
   async function background_nopopup() {
     browser.test.log("nopopup background script ran");
     browser.composeAction.onClicked.addListener(async (tab, info) => {
       browser.test.assertEq("object", typeof tab);
       browser.test.assertEq("object", typeof info);
       browser.test.assertEq(0, info.button);
       browser.test.assertTrue(Array.isArray(info.modifiers));
@@ -176,27 +142,27 @@ add_task(async function setup() {
       },
       compose_action: {
         default_title: "This is a test",
       },
     },
     useAddonManager: "temporary",
   };
 
-  await test_it(extensionDetails, "composeToolbar2");
+  await test_it(account, extensionDetails, "composeToolbar2");
 
   extensionDetails.background = background_popup;
   extensionDetails.manifest.compose_action.default_popup = "popup.html";
-  await test_it(extensionDetails, "composeToolbar2");
+  await test_it(account, extensionDetails, "composeToolbar2");
 
   extensionDetails.background = background_nopopup;
   extensionDetails.manifest.compose_action.default_area = "formattoolbar";
   delete extensionDetails.manifest.compose_action.default_popup;
-  await test_it(extensionDetails, "FormatToolbar");
+  await test_it(account, extensionDetails, "FormatToolbar");
 
   extensionDetails.background = background_popup;
   extensionDetails.manifest.compose_action.default_popup = "popup.html";
-  await test_it(extensionDetails, "FormatToolbar");
+  await test_it(account, extensionDetails, "FormatToolbar");
 
   Services.xulStore.removeDocument(
     "chrome://messenger/content/messengercompose/messengercompose.xhtml"
   );
 });
--- a/mail/components/extensions/test/browser/browser_ext_messageDisplayAction.js
+++ b/mail/components/extensions/test/browser/browser_ext_messageDisplayAction.js
@@ -129,34 +129,17 @@ add_task(async () => {
   extension = ExtensionTestUtils.loadExtension(extensionDetails);
   window.MsgOpenSelectedMessages();
   await test_it(extension, window);
   document.getElementById("tabmail").closeTab();
 
   info("Message window, no pop-up");
 
   extension = ExtensionTestUtils.loadExtension(extensionDetails);
-  let messageWindowPromise = BrowserTestUtils.domWindowOpened(
-    undefined,
-    async win => {
-      await BrowserTestUtils.waitForEvent(win, "load");
-      if (
-        win.document.documentURI !=
-        "chrome://messenger/content/messageWindow.xhtml"
-      ) {
-        return false;
-      }
-      await BrowserTestUtils.browserLoaded(
-        win.document.getElementById("messagepane")
-      );
-      return true;
-    }
-  );
-  window.MsgOpenNewWindowForMessage();
-  let messageWindow = await messageWindowPromise;
+  let messageWindow = await openNewWindowForMessage();
   await test_it(extension, messageWindow);
   messageWindow.close();
 
   info("3-pane tab, with pop-up");
 
   extensionDetails.background = background_popup;
   extensionDetails.manifest.message_display_action.default_popup = "popup.html";
   extension = ExtensionTestUtils.loadExtension(extensionDetails);
@@ -167,29 +150,12 @@ add_task(async () => {
   extension = ExtensionTestUtils.loadExtension(extensionDetails);
   window.MsgOpenSelectedMessages();
   await test_it(extension, window);
   document.getElementById("tabmail").closeTab();
 
   info("Message window, with pop-up");
 
   extension = ExtensionTestUtils.loadExtension(extensionDetails);
-  messageWindowPromise = BrowserTestUtils.domWindowOpened(
-    undefined,
-    async win => {
-      await BrowserTestUtils.waitForEvent(win, "load");
-      if (
-        win.document.documentURI !=
-        "chrome://messenger/content/messageWindow.xhtml"
-      ) {
-        return false;
-      }
-      await BrowserTestUtils.browserLoaded(
-        win.document.getElementById("messagepane")
-      );
-      return true;
-    }
-  );
-  window.MsgOpenNewWindowForMessage();
-  messageWindow = await messageWindowPromise;
+  messageWindow = await openNewWindowForMessage();
   await test_it(extension, messageWindow);
   messageWindow.close();
 });
--- a/mail/components/extensions/test/browser/head.js
+++ b/mail/components/extensions/test/browser/head.js
@@ -263,16 +263,66 @@ async function openNewMailWindow(options
   await Promise.all([
     BrowserTestUtils.waitForEvent(win, "focus", true),
     BrowserTestUtils.waitForEvent(win, "activate", true),
   ]);
 
   return win;
 }
 
+async function openComposeWindow(account) {
+  let params = Cc[
+    "@mozilla.org/messengercompose/composeparams;1"
+  ].createInstance(Ci.nsIMsgComposeParams);
+  let composeFields = Cc[
+    "@mozilla.org/messengercompose/composefields;1"
+  ].createInstance(Ci.nsIMsgCompFields);
+
+  params.identity = account.defaultIdentity;
+  params.composeFields = composeFields;
+
+  let composeWindowPromise = BrowserTestUtils.domWindowOpened(
+    undefined,
+    async win => {
+      await BrowserTestUtils.waitForEvent(win, "load");
+      if (
+        win.document.documentURI !=
+        "chrome://messenger/content/messengercompose/messengercompose.xhtml"
+      ) {
+        return false;
+      }
+      await BrowserTestUtils.waitForEvent(win, "compose-editor-ready");
+      return true;
+    }
+  );
+  MailServices.compose.OpenComposeWindowWithParams(null, params);
+  return composeWindowPromise;
+}
+
+async function openNewWindowForMessage(msg) {
+  let messageWindowPromise = BrowserTestUtils.domWindowOpened(
+    undefined,
+    async win => {
+      await BrowserTestUtils.waitForEvent(win, "load");
+      if (
+        win.document.documentURI !=
+        "chrome://messenger/content/messageWindow.xhtml"
+      ) {
+        return false;
+      }
+      await BrowserTestUtils.browserLoaded(
+        win.document.getElementById("messagepane")
+      );
+      return true;
+    }
+  );
+  window.MsgOpenNewWindowForMessage(msg);
+  return messageWindowPromise;
+}
+
 /**
  * Check the headers of an open compose window against expected values.
  *
  * @param {Object} expected - A dictionary of expected headers.
  *    Omit headers that should have no value.
  * @param {string[]} [fields.to]
  * @param {string[]} [fields.cc]
  * @param {string[]} [fields.bcc]