Bug 1631995 - Test events in the browser.windows and browser.tabs APIs and fix things that fail. r=mkmelin DONTBUILD
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 21 Apr 2020 17:15:24 +1200
changeset 38037 a15a99bbf363b7478eb5cdad526e391b586189f0
parent 38036 e8fdfd14d5567ea7fc380a84693022ca84a15493
child 38038 bf4e3f27671f31a5e26aaebfc5e3f8b15492e27f
push id2595
push userclokep@gmail.com
push dateMon, 04 May 2020 19:02:04 +0000
treeherdercomm-beta@f53913797371 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1631995
Bug 1631995 - Test events in the browser.windows and browser.tabs APIs and fix things that fail. r=mkmelin DONTBUILD
mail/base/content/tabmail.js
mail/components/extensions/parent/ext-mail.js
mail/components/extensions/test/browser/browser.ini
mail/components/extensions/test/browser/browser_ext_windows_events.js
--- a/mail/base/content/tabmail.js
+++ b/mail/base/content/tabmail.js
@@ -867,16 +867,23 @@
         tabOpenFirstFunc.call(firstTab.mode.tabType, firstTab);
         this.setTabTitle(null);
         for (let tabMonitor of this.tabMonitors) {
           if ("onTabOpened" in tabMonitor) {
             tabMonitor.onTabOpened(firstTab, true);
           }
           tabMonitor.onTabSwitched(firstTab, null);
         }
+
+        // Dispatch tab opening event
+        let evt = new CustomEvent("TabOpen", {
+          bubbles: true,
+          detail: { tabInfo: firstTab, moving: false },
+        });
+        document.querySelector("#tabmail-tabs tab").dispatchEvent(evt);
       }
     }
 
     // eslint-disable-next-line complexity
     openTab(aTabModeName, aArgs) {
       try {
         if (!(aTabModeName in this.tabModes)) {
           throw new Error("No such tab mode: " + aTabModeName);
--- a/mail/components/extensions/parent/ext-mail.js
+++ b/mail/components/extensions/parent/ext-mail.js
@@ -549,16 +549,28 @@ class TabTracker extends TabTrackerBase 
 
   /**
    * A private method which is called whenever a new mail window is opened, and dispatches the
    * necessary events for it.
    *
    * @param {DOMWindow} window      The window being opened.
    */
   _handleWindowOpen(window) {
+    if (
+      ["mail:addressbook", "msgcompose", "mail:messageWindow"].includes(
+        window.document.documentElement.getAttribute("windowtype")
+      )
+    ) {
+      this.emit("tab-created", {
+        nativeTabInfo: window,
+        currentTab: window,
+      });
+      return;
+    }
+
     let tabmail = window.document.getElementById("tabmail");
     if (!tabmail) {
       return;
     }
 
     for (let nativeTabInfo of tabmail.tabInfo) {
       if (!getTabBrowser(nativeTabInfo)) {
         continue;
@@ -569,16 +581,30 @@ class TabTracker extends TabTrackerBase 
 
   /**
    * A private method which is called whenever a mail window is closed, and dispatches the necessary
    * events for it.
    *
    * @param {DOMWindow} window      The window being closed.
    */
   _handleWindowClose(window) {
+    if (
+      ["mail:addressbook", "msgcompose", "mail:messageWindow"].includes(
+        window.document.documentElement.getAttribute("windowtype")
+      )
+    ) {
+      this.emit("tab-removed", {
+        window,
+        tabId: this.getId(window),
+        windowId: windowTracker.getId(getTabWindow(window)),
+        isWindowClosing: true,
+      });
+      return;
+    }
+
     let tabmail = window.document.getElementById("tabmail");
     if (!tabmail) {
       return;
     }
 
     for (let nativeTabInfo of tabmail.tabInfo) {
       if (!getTabBrowser(nativeTabInfo)) {
         continue;
--- a/mail/components/extensions/test/browser/browser.ini
+++ b/mail/components/extensions/test/browser/browser.ini
@@ -30,9 +30,10 @@ support-files = data/content.html
 [browser_ext_menus_replace_menu.js]
 [browser_ext_menus_replace_menu_context.js]
 [browser_ext_messageDisplay.js]
 [browser_ext_messageDisplayAction.js]
 [browser_ext_messageDisplayAction_properties.js]
 [browser_ext_quickFilter.js]
 [browser_ext_tabs_events.js]
 [browser_ext_windows.js]
+[browser_ext_windows_events.js]
 [browser_ext_windows_types.js]
new file mode 100644
--- /dev/null
+++ b/mail/components/extensions/test/browser/browser_ext_windows_events.js
@@ -0,0 +1,221 @@
+/* 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/. */
+
+/* globals gFolderDisplay, gFolderTreeView, MsgOpenNewWindowForFolder, MsgOpenNewWindowForMessage */
+
+add_task(async () => {
+  let account = createAccount();
+  addIdentity(account);
+  let rootFolder = account.incomingServer.rootFolder;
+  rootFolder.createSubfolder("windowsEvents", null);
+  let testFolder = rootFolder.findSubFolder("windowsEvents");
+  createMessages(testFolder, 5);
+
+  gFolderTreeView.selectFolder(testFolder);
+  gFolderDisplay.selectViewIndex(0);
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background: async () => {
+      let listener = {
+        tabEvents: [],
+        windowEvents: [],
+        currentPromise: null,
+
+        pushEvent(...args) {
+          browser.test.log(JSON.stringify(args));
+          let queue = args[0].startsWith("windows.")
+            ? this.windowEvents
+            : this.tabEvents;
+          queue.push(args);
+          if (queue.currentPromise) {
+            let p = queue.currentPromise;
+            queue.currentPromise = null;
+            p.resolve();
+          }
+        },
+        windowsOnCreated(...args) {
+          this.pushEvent("windows.onCreated", ...args);
+        },
+        windowsOnRemoved(...args) {
+          this.pushEvent("windows.onRemoved", ...args);
+        },
+        tabsOnCreated(...args) {
+          this.pushEvent("tabs.onCreated", ...args);
+        },
+        tabsOnRemoved(...args) {
+          this.pushEvent("tabs.onRemoved", ...args);
+        },
+        async checkEvent(expectedEvent, ...expectedArgs) {
+          let queue = expectedEvent.startsWith("windows.")
+            ? this.windowEvents
+            : this.tabEvents;
+          if (queue.length == 0) {
+            await new Promise(resolve => (queue.currentPromise = { resolve }));
+          }
+          let [actualEvent, ...actualArgs] = queue.shift();
+          browser.test.assertEq(expectedEvent, actualEvent);
+          browser.test.assertEq(expectedArgs.length, actualArgs.length);
+
+          for (let i = 0; i < expectedArgs.length; i++) {
+            browser.test.assertEq(typeof expectedArgs[i], typeof actualArgs[i]);
+            if (typeof expectedArgs[i] == "object") {
+              for (let key of Object.keys(expectedArgs[i])) {
+                browser.test.assertEq(expectedArgs[i][key], actualArgs[i][key]);
+              }
+            } else {
+              browser.test.assertEq(expectedArgs[i], actualArgs[i]);
+            }
+          }
+
+          return actualArgs;
+        },
+      };
+      browser.tabs.onCreated.addListener(listener.tabsOnCreated.bind(listener));
+      browser.tabs.onRemoved.addListener(listener.tabsOnRemoved.bind(listener));
+      browser.windows.onCreated.addListener(
+        listener.windowsOnCreated.bind(listener)
+      );
+      browser.windows.onRemoved.addListener(
+        listener.windowsOnRemoved.bind(listener)
+      );
+
+      browser.test.log(
+        "Collect the ID of the initial window (there must be only one) and tab."
+      );
+
+      let initialWindows = await browser.windows.getAll({ populate: true });
+      browser.test.assertEq(1, initialWindows.length);
+      let [{ id: initialWindow, tabs: initialTabs }] = initialWindows;
+      browser.test.assertEq(1, initialTabs.length);
+      browser.test.assertEq(0, initialTabs[0].index);
+      browser.test.assertTrue(initialTabs[0].mailTab);
+      let [{ id: initialTab }] = initialTabs;
+
+      browser.test.log("Open a new main window (messenger.xhtml).");
+
+      browser.test.sendMessage("openMainWindow");
+      let [{ id: mainWindow }] = await listener.checkEvent(
+        "windows.onCreated",
+        { type: "normal" }
+      );
+      let [{ id: mainTab }] = await listener.checkEvent("tabs.onCreated", {
+        index: 0,
+        windowId: mainWindow,
+        active: true,
+        mailTab: true,
+      });
+
+      browser.test.log("Open the address book window (addressbook.xhtml).");
+
+      await browser.addressBooks.openUI();
+      let [{ id: addressBookWindow }] = await listener.checkEvent(
+        "windows.onCreated",
+        {
+          type: "addressBook",
+        }
+      );
+      let [{ id: addressBookTab }] = await listener.checkEvent(
+        "tabs.onCreated",
+        { index: 0, windowId: addressBookWindow, active: true, mailTab: false }
+      );
+
+      browser.test.log("Open a compose window (messengercompose.xhtml).");
+
+      await browser.compose.beginNew();
+      let [{ id: composeWindow }] = await listener.checkEvent(
+        "windows.onCreated",
+        {
+          type: "messageCompose",
+        }
+      );
+      let [{ id: composeTab }] = await listener.checkEvent("tabs.onCreated", {
+        index: 0,
+        windowId: composeWindow,
+        active: true,
+        mailTab: false,
+      });
+
+      browser.test.log("Open a message in a window (messageWindow.xhtml).");
+
+      browser.test.sendMessage("openDisplayWindow");
+      let [{ id: displayWindow }] = await listener.checkEvent(
+        "windows.onCreated",
+        {
+          type: "messageDisplay",
+        }
+      );
+      let [{ id: displayTab }] = await listener.checkEvent("tabs.onCreated", {
+        index: 0,
+        windowId: displayWindow,
+        active: true,
+        mailTab: false,
+      });
+
+      browser.test.log("Pause to lets windows load properly.");
+
+      // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+      await new Promise(resolve => setTimeout(resolve, 2500));
+
+      browser.test.log("Close the new main window.");
+
+      await browser.windows.remove(mainWindow);
+      await listener.checkEvent("windows.onRemoved", mainWindow);
+      await listener.checkEvent("tabs.onRemoved", mainTab, {
+        windowId: mainWindow,
+        isWindowClosing: true,
+      });
+
+      browser.test.log("Close the address book window.");
+
+      await browser.addressBooks.closeUI();
+      await listener.checkEvent("windows.onRemoved", addressBookWindow);
+      await listener.checkEvent("tabs.onRemoved", addressBookTab, {
+        windowId: addressBookWindow,
+        isWindowClosing: true,
+      });
+
+      browser.test.log("Close the compose window.");
+
+      await browser.windows.remove(composeWindow);
+      await listener.checkEvent("windows.onRemoved", composeWindow);
+      await listener.checkEvent("tabs.onRemoved", composeTab, {
+        windowId: composeWindow,
+        isWindowClosing: true,
+      });
+
+      browser.test.log("Close the message window.");
+
+      await browser.windows.remove(displayWindow);
+      await listener.checkEvent("windows.onRemoved", displayWindow);
+      await listener.checkEvent("tabs.onRemoved", displayTab, {
+        windowId: displayWindow,
+        isWindowClosing: true,
+      });
+
+      let finalWindows = await browser.windows.getAll({ populate: true });
+      browser.test.assertEq(1, finalWindows.length);
+      browser.test.assertEq(initialWindow, finalWindows[0].id);
+      browser.test.assertEq(1, finalWindows[0].tabs.length);
+      browser.test.assertEq(initialTab, finalWindows[0].tabs[0].id);
+
+      browser.test.assertEq(0, listener.tabEvents.length);
+      browser.test.assertEq(0, listener.windowEvents.length);
+      browser.test.notifyPass("finished");
+    },
+    manifest: {
+      permissions: ["addressBooks"],
+    },
+  });
+
+  await extension.startup();
+
+  await extension.awaitMessage("openMainWindow");
+  MsgOpenNewWindowForFolder(testFolder.URI);
+
+  await extension.awaitMessage("openDisplayWindow");
+  MsgOpenNewWindowForMessage();
+
+  await extension.awaitFinish("finished");
+  await extension.unload();
+});