Bug 1414379 - Closing the extension popup tab should select the parent tab only if the popup tab was selected. r=mixedpuppy
authorLuca Greco <lgreco@mozilla.com>
Wed, 01 Nov 2017 04:45:50 +0100
changeset 390522 ec266337fc47fa3237579b50ba92663b479a91ee
parent 390521 294c88b3bd2cdb6592b1decc03c54f3931474447
child 390523 02c14327d1451d54d4e81da141453a93784848cc
push id32836
push userebalazs@mozilla.com
push dateTue, 07 Nov 2017 21:56:07 +0000
treeherdermozilla-central@fb76bb12880c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1414379
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1414379 - Closing the extension popup tab should select the parent tab only if the popup tab was selected. r=mixedpuppy MozReview-Commit-ID: KgSTdblvmYe
mobile/android/components/extensions/ext-utils.js
mobile/android/components/extensions/test/mochitest/chrome.ini
mobile/android/components/extensions/test/mochitest/test_ext_popup_behavior.html
--- a/mobile/android/components/extensions/ext-utils.js
+++ b/mobile/android/components/extensions/ext-utils.js
@@ -249,16 +249,18 @@ global.WindowEventManager = class extend
 };
 
 class TabTracker extends TabTrackerBase {
   constructor() {
     super();
 
     // Keep track of the extension popup tab.
     this._extensionPopupTabWeak = null;
+    // Keep track of the selected tabId
+    this._selectedTabId = null;
   }
 
   init() {
     if (this.initialized) {
       return;
     }
     this.initialized = true;
 
@@ -366,16 +368,18 @@ class TabTracker extends TabTrackerBase 
    * @param {string} event The event which fired.
    * @param {object} data Information about the event which fired.
    */
   onEvent(event, data) {
     const {BrowserApp} = windowTracker.topWindow;
 
     switch (event) {
       case "Tab:Selected": {
+        this._selectedTabId = data.id;
+
         // If a new tab has been selected while an extension popup tab is still open,
         // close it immediately.
         const nativeTab = BrowserApp.getTabForId(data.id);
 
         const popupTab = tabTracker.extensionPopupTab;
         if (popupTab && popupTab !== nativeTab) {
           BrowserApp.closeTab(popupTab);
         }
@@ -408,16 +412,22 @@ class TabTracker extends TabTrackerBase 
    */
   emitRemoved(nativeTab, isWindowClosing) {
     let windowId = windowTracker.getId(nativeTab.browser.ownerGlobal);
     let tabId = this.getId(nativeTab);
 
     if (this.extensionPopupTab && this.extensionPopupTab === nativeTab) {
       this._extensionPopupTabWeak = null;
 
+      // Do not switch to the parent tab of the extension popup tab
+      // if the popup tab is not the selected tab.
+      if (this._selectedTabId !== tabId) {
+        return;
+      }
+
       // Select the parent tab when the closed tab was an extension popup tab.
       const {BrowserApp} = windowTracker.topWindow;
       const popupParentTab = BrowserApp.getTabForId(nativeTab.parentId);
       if (popupParentTab) {
         BrowserApp.selectTab(popupParentTab);
       }
     }
 
--- a/mobile/android/components/extensions/test/mochitest/chrome.ini
+++ b/mobile/android/components/extensions/test/mochitest/chrome.ini
@@ -11,8 +11,9 @@ tags = webextensions
 [test_ext_browsingData_cookies_cache.html]
 [test_ext_browsingData_downloads.html]
 [test_ext_browsingData_formdata.html]
 [test_ext_browsingData_settings.html]
 [test_ext_options_ui.html]
 [test_ext_pageAction_show_hide.html]
 [test_ext_pageAction_getPopup_setPopup.html]
 skip-if = os == 'android' # bug 1373170
+[test_ext_popup_behavior.html]
new file mode 100644
--- /dev/null
+++ b/mobile/android/components/extensions/test/mochitest/test_ext_popup_behavior.html
@@ -0,0 +1,180 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>PageAction Test</title>
+  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
+  <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+var {BrowserActions} = SpecialPowers.Cu.import("resource://gre/modules/BrowserActions.jsm", {});
+var {Services} = SpecialPowers.Cu.import("resource://gre/modules/Services.jsm", {});
+
+function promiseDispatchedWindowEvent(eventName) {
+  return new Promise(resolve => {
+    let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+    let WindowEventDispatcher = chromeWin.WindowEventDispatcher;
+
+    let listener = (event) => {
+      WindowEventDispatcher.unregisterListener(listener, eventName);
+      resolve();
+    };
+
+    WindowEventDispatcher.registerListener(listener, eventName);
+  });
+}
+
+async function closeTabAndWaitTabClosed({BrowserApp, tab}) {
+  let onceTabClosed = promiseDispatchedWindowEvent("Tab:Closed");
+  BrowserApp.closeTab(tab);
+  await onceTabClosed;
+}
+
+async function selectTabAndWaitTabSelected({BrowserApp, tab}) {
+  let onceTabSelected = promiseDispatchedWindowEvent("Tab:Selected");
+  BrowserApp.selectTab(tab);
+  await onceTabSelected;
+}
+
+add_task(async function test_popup_behavior() {
+  const chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
+  const BrowserApp = chromeWin.BrowserApp;
+
+  async function background() {
+    const tab1 = await browser.tabs.create({url: "http://example.com#test_popup_behavior_1"});
+    const tab2 = await browser.tabs.create({url: "http://example.com#test_popup_behavior_2"});
+    const tab3 = await browser.tabs.create({url: "http://example.com#test_popup_behavior_3"});
+
+    browser.tabs.onUpdated.addListener((tabId, changeInfo) => {
+      if (![tab1.id, tab2.id, tab3.id].includes(tabId) ||
+          changeInfo.status !== "complete") {
+        return;
+      }
+
+      browser.test.sendMessage("page-loaded", tabId);
+    });
+
+    browser.test.sendMessage("background_page.ready", {
+      tabId1: tab1.id,
+      tabId2: tab2.id,
+      tabId3: tab3.id,
+    });
+  }
+
+  async function popupScript() {
+    browser.test.sendMessage("popup_script.loaded");
+  }
+
+  let popupHtml = `<!DOCTYPE html>
+    <html>
+      <head>
+        <meta charset="utf-8">
+      </head>
+      <body>
+        <h1>Extension Popup</h1>
+        <script src="popup.js"><\/script>
+      </body>
+    </html>
+  `;
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      "name": "BrowserAction Extension",
+      "browser_action": {
+        "default_title": "Browser Action",
+        "default_popup": "popup.html",
+        "default_icon": {
+          "18": "extension.png",
+        },
+      },
+      "permissions": ["activeTab"],
+    },
+    files: {
+      "popup.html": popupHtml,
+      "popup.js": popupScript,
+    },
+  });
+
+  await extension.startup();
+
+  const {
+    tabId1,
+    tabId2,
+    tabId3,
+  } = await extension.awaitMessage("background_page.ready");
+
+  const uuid = `{${extension.uuid}}`;
+
+  ok(BrowserActions.isShown(uuid), "browser action is shown");
+
+  info("Wait the new tabs to be loaded");
+
+  await extension.awaitMessage("page-loaded");
+  await extension.awaitMessage("page-loaded");
+  await extension.awaitMessage("page-loaded");
+
+  is(BrowserApp.selectedTab.id, tabId3, "The third of the new tabs has been selected");
+
+  BrowserActions.synthesizeClick(uuid);
+  await extension.awaitMessage("popup_script.loaded");
+
+  // Check that while the extension popup tab is selected the active tab is still the tab
+  // from which the user has opened the extension popup.
+  ok(BrowserApp.selectedBrowser.currentURI.spec.endsWith("popup.html"),
+     "The first popup tab has been selected");
+
+  let popupParentTabId = BrowserApp.selectedTab.parentId;
+  is(popupParentTabId, tabId3, "The parent of the first popup tab is the third tab");
+
+  // Close the popup and test that its parent tab is selected.
+  await closeTabAndWaitTabClosed({BrowserApp, tab: BrowserApp.selectedTab});
+
+  const tab1 = BrowserApp.getTabForId(tabId1);
+  const tab2 = BrowserApp.getTabForId(tabId2);
+  const tab3 = BrowserApp.getTabForId(tabId3);
+
+  BrowserActions.synthesizeClick(uuid);
+  await extension.awaitMessage("popup_script.loaded");
+
+  ok(BrowserApp.selectedBrowser.currentURI.spec.endsWith("popup.html"),
+     "The second popup tab has been selected");
+
+  popupParentTabId = BrowserApp.selectedTab.parentId;
+  is(popupParentTabId, tabId3, "The parent of the second popup tab is the third tab");
+
+  // Switch to the second tab and expect the popup tab to be closed.
+  let onceTabClosed = promiseDispatchedWindowEvent("Tab:Closed");
+  await Promise.all([
+    selectTabAndWaitTabSelected({BrowserApp, tab: tab2}),
+    onceTabClosed,
+  ]);
+
+  // Switch to the first tab and expect it to be the next selected tab.
+  // (which ensure that Bug 1373170 has been fixed and the closed popup tab
+  // has not selected its parent tab).
+  await selectTabAndWaitTabSelected({BrowserApp, tab: tab1});
+
+  is(BrowserApp.selectedTab.id, tabId1,
+     "The first tab is the currently selected tab");
+
+  // Close all the tabs before exiting the test.
+  await Promise.all([
+    closeTabAndWaitTabClosed({BrowserApp, tab: tab1}),
+    closeTabAndWaitTabClosed({BrowserApp, tab: tab2}),
+    closeTabAndWaitTabClosed({BrowserApp, tab: tab3}),
+  ]);
+
+  await extension.unload();
+});
+
+</script>
+
+</body>
+</html>