mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	Bug 1966829 - Add a Taskbar Tabs window manager. r=mossop,cdupuis,frontend-codestyle-reviewers
This class manages the creation and removal of Taskbar Tabs windows. This maintains state necessary to determine what window a tab should return to if ejected from Taskbar Tabs. Differential Revision: https://phabricator.services.mozilla.com/D249717
This commit is contained in:
		
							parent
							
								
									771370ff73
								
							
						
					
					
						commit
						21c6063d56
					
				
					 7 changed files with 469 additions and 55 deletions
				
			
		
							
								
								
									
										231
									
								
								browser/components/taskbartabs/TaskbarTabsWindowManager.sys.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								browser/components/taskbartabs/TaskbarTabsWindowManager.sys.mjs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,231 @@
 | 
				
			||||||
 | 
					/* vim: se cin sw=2 ts=2 et filetype=javascript :
 | 
				
			||||||
 | 
					 * 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/. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const kTaskbarTabsWindowFeatures =
 | 
				
			||||||
 | 
					  "titlebar,close,toolbar,location,personalbar=no,status,menubar=no,resizable,minimizable,scrollbars";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let lazy = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChromeUtils.defineESModuleGetters(lazy, {
 | 
				
			||||||
 | 
					  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
 | 
				
			||||||
 | 
					  TaskbarTabsUtils: "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs",
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					XPCOMUtils.defineLazyServiceGetters(lazy, {
 | 
				
			||||||
 | 
					  WinTaskbar: ["@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChromeUtils.defineLazyGetter(lazy, "logConsole", () => {
 | 
				
			||||||
 | 
					  return console.createInstance({
 | 
				
			||||||
 | 
					    prefix: "TaskbarTabs",
 | 
				
			||||||
 | 
					    maxLogLevel: "Warn",
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Manager for the lifetimes of Taskbar Tab windows.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export class TaskbarTabsWindowManager {
 | 
				
			||||||
 | 
					  // Count of active taskbar tabs associated to an ID.
 | 
				
			||||||
 | 
					  #tabIdCount = new Map();
 | 
				
			||||||
 | 
					  // Map from the tab browser permanent key to originating window ID.
 | 
				
			||||||
 | 
					  #tabOriginMap = new WeakMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Moves an existing browser tab into a Taskbar Tab.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to replace the window with.
 | 
				
			||||||
 | 
					   * @param {string} aTaskbarTab.id - ID of the Taskbar Tab.
 | 
				
			||||||
 | 
					   * @param {MozTabbrowserTab} aTab - The tab to adopt as a Taskbar Tab.
 | 
				
			||||||
 | 
					   * @returns {Promise} Resolves once the tab replacing window has openend.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async replaceTabWithWindow({ id }, aTab) {
 | 
				
			||||||
 | 
					    let originWindow = aTab.ownerGlobal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save the parent window of this tab, so we can revert back if needed.
 | 
				
			||||||
 | 
					    let tabId = getTabId(aTab);
 | 
				
			||||||
 | 
					    let windowId = getWindowId(originWindow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
 | 
				
			||||||
 | 
					      Ci.nsIWritablePropertyBag2
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    extraOptions.setPropertyAsAString("taskbartab", id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 | 
				
			||||||
 | 
					    args.appendElement(aTab);
 | 
				
			||||||
 | 
					    args.appendElement(extraOptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.#openWindow(id, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.#tabOriginMap.set(tabId, windowId);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Opens a new Taskbar Tab Window.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {TaskbarTab} aTaskbarTab - The Taskbar Tab to open.
 | 
				
			||||||
 | 
					   * @returns {Promise} Resolves once the window has opened.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async openWindow(aTaskbarTab) {
 | 
				
			||||||
 | 
					    let url = Cc["@mozilla.org/supports-string;1"].createInstance(
 | 
				
			||||||
 | 
					      Ci.nsISupportsString
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    url.data = aTaskbarTab.startUrl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
 | 
				
			||||||
 | 
					      Ci.nsIWritablePropertyBag2
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    extraOptions.setPropertyAsAString("taskbartab", aTaskbarTab.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let userContextId = Cc["@mozilla.org/supports-PRUint32;1"].createInstance(
 | 
				
			||||||
 | 
					      Ci.nsISupportsPRUint32
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    userContextId.data = aTaskbarTab.userContextId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 | 
				
			||||||
 | 
					    args.appendElement(url);
 | 
				
			||||||
 | 
					    args.appendElement(extraOptions);
 | 
				
			||||||
 | 
					    args.appendElement(null);
 | 
				
			||||||
 | 
					    args.appendElement(null);
 | 
				
			||||||
 | 
					    args.appendElement(undefined);
 | 
				
			||||||
 | 
					    args.appendElement(userContextId);
 | 
				
			||||||
 | 
					    args.appendElement(null);
 | 
				
			||||||
 | 
					    args.appendElement(null);
 | 
				
			||||||
 | 
					    args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.#openWindow(aTaskbarTab.id, args);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Handles common window opening behavior for Taskbar Tabs.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {string} aId - ID of the Taskbar Tab to use as the window AUMID.
 | 
				
			||||||
 | 
					   * @param {nsIMutableArray} aArgs - `args` to pass to the opening window.
 | 
				
			||||||
 | 
					   * @returns {Promise} Resolves once window has opened and tab count has been
 | 
				
			||||||
 | 
					   *                    incremented.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async #openWindow(aId, aArgs) {
 | 
				
			||||||
 | 
					    let win = await lazy.BrowserWindowTracker.promiseOpenWindow({
 | 
				
			||||||
 | 
					      args: aArgs,
 | 
				
			||||||
 | 
					      features: kTaskbarTabsWindowFeatures,
 | 
				
			||||||
 | 
					      all: false,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lazy.WinTaskbar.setGroupIdForWindow(win, aId);
 | 
				
			||||||
 | 
					    win.focus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let tabIdCount = this.#tabIdCount.get(aId) ?? 0;
 | 
				
			||||||
 | 
					    this.#tabIdCount.set(aId, ++tabIdCount);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Reverts a web app to a tab in a regular Firefox window. We will try to use
 | 
				
			||||||
 | 
					   * the window the taskbar tab originated from, if that's not avaliable, we
 | 
				
			||||||
 | 
					   * will use the most recently active window. If no window is avalaible, a new
 | 
				
			||||||
 | 
					   * one will be opened.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {DOMWindow} aWindow - A Tasbkar Tab window.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  async ejectWindow(aWindow) {
 | 
				
			||||||
 | 
					    lazy.logConsole.info("Ejecting window from Taskbar Tabs.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let taskbarTabId = lazy.TaskbarTabsUtils.getTaskbarTabIdFromWindow(aWindow);
 | 
				
			||||||
 | 
					    if (!taskbarTabId) {
 | 
				
			||||||
 | 
					      throw new Error("No Taskbar Tab ID found on window.");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      lazy.logConsole.debug(`Taskbar Tab ID is ${taskbarTabId}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let windowList = lazy.BrowserWindowTracker.getOrderedWindows({
 | 
				
			||||||
 | 
					      private: false,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // A Taskbar Tab should only contain one tab, but iterate over the browser's
 | 
				
			||||||
 | 
					    // tabs just in case one snuck in.
 | 
				
			||||||
 | 
					    for (const tab of aWindow.gBrowser.tabs) {
 | 
				
			||||||
 | 
					      let tabId = getTabId(tab);
 | 
				
			||||||
 | 
					      let originWindowId = this.#tabOriginMap.get(tabId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let win =
 | 
				
			||||||
 | 
					        // Find the originating window for the Taskbar Tab if it still exists.
 | 
				
			||||||
 | 
					        windowList.find(window => {
 | 
				
			||||||
 | 
					          let windowId = getWindowId(window);
 | 
				
			||||||
 | 
					          let matching = windowId === originWindowId;
 | 
				
			||||||
 | 
					          if (matching) {
 | 
				
			||||||
 | 
					            lazy.logConsole.debug(
 | 
				
			||||||
 | 
					              `Ejecting into originating window: ${windowId}`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return matching;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!win) {
 | 
				
			||||||
 | 
					        // Otherwise the most recent non-Taskbar Tabs window interacted with.
 | 
				
			||||||
 | 
					        win = lazy.BrowserWindowTracker.getTopWindow({
 | 
				
			||||||
 | 
					          private: false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (win) {
 | 
				
			||||||
 | 
					          lazy.logConsole.debug(`Ejecting into top window.`);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (win) {
 | 
				
			||||||
 | 
					        // Set this tab to the last tab position of the window.
 | 
				
			||||||
 | 
					        win.gBrowser.adoptTab(tab, {
 | 
				
			||||||
 | 
					          tabIndex: win.gBrowser.openTabs.length,
 | 
				
			||||||
 | 
					          selectTab: true,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        lazy.logConsole.debug(
 | 
				
			||||||
 | 
					          "No originating or existing browser window found, ejecting into newly created window."
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        win = await lazy.BrowserWindowTracker.promiseOpenWindow({ args: tab });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      win.focus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.#tabOriginMap.delete(tabId);
 | 
				
			||||||
 | 
					      let tabIdCount = this.#tabIdCount.get(taskbarTabId);
 | 
				
			||||||
 | 
					      if (tabIdCount > 0) {
 | 
				
			||||||
 | 
					        this.#tabIdCount.set(taskbarTabId, --tabIdCount);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        lazy.logConsole.error("Tab count should have been greater than 0.");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Returns a count of the current windows associated to a Taskbar Tab.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param {string} aId - The Taskbar Tab ID.
 | 
				
			||||||
 | 
					   * @returns {integer} Count of windows associated to the Taskbar Tab ID.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  getCountForId(aId) {
 | 
				
			||||||
 | 
					    return this.#tabIdCount.get(aId) ?? 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Retrieves the browser tab's ID.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {MozTabbrowserTab} aTab - Tab to retrieve the ID from.
 | 
				
			||||||
 | 
					 * @returns {object} The permanent key identifying the tab.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getTabId(aTab) {
 | 
				
			||||||
 | 
					  return aTab.permanentKey;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Retrieves the window ID.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {DOMWindow} aWindow
 | 
				
			||||||
 | 
					 * @returns {string} A unique string identifying the window.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getWindowId(aWindow) {
 | 
				
			||||||
 | 
					  return aWindow.docShell.outerWindowID;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ EXTRA_JS_MODULES.taskbartabs += [
 | 
				
			||||||
    "TaskbarTabUI.sys.mjs",
 | 
					    "TaskbarTabUI.sys.mjs",
 | 
				
			||||||
    "TaskbarTabsRegistry.sys.mjs",
 | 
					    "TaskbarTabsRegistry.sys.mjs",
 | 
				
			||||||
    "TaskbarTabsUtils.sys.mjs",
 | 
					    "TaskbarTabsUtils.sys.mjs",
 | 
				
			||||||
 | 
					    "TaskbarTabsWindowManager.sys.mjs",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
JAR_MANIFESTS += ["jar.mn"]
 | 
					JAR_MANIFESTS += ["jar.mn"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,4 +19,7 @@ support-files = [
 | 
				
			||||||
  "dummy_page.html",
 | 
					  "dummy_page.html",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					["browser_taskbarTabs_windowManager.js"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
["browser_taskbarTabs_windowTracker.js"]
 | 
					["browser_taskbarTabs_windowTracker.js"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,10 +2,6 @@
 | 
				
			||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
					http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
				
			||||||
"use strict";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ChromeUtils.defineESModuleGetters(this, {
 | 
					 | 
				
			||||||
  AppConstants: "resource://gre/modules/AppConstants.sys.mjs",
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Given a window, check if it meets all requirements
 | 
					// Given a window, check if it meets all requirements
 | 
				
			||||||
// of the taskbar tab chrome UI
 | 
					// of the taskbar tab chrome UI
 | 
				
			||||||
function checkWindowChrome(win) {
 | 
					function checkWindowChrome(win) {
 | 
				
			||||||
| 
						 | 
					@ -104,33 +100,12 @@ async function checkHamburgerMenu(win) {
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_task(async function testWindowChrome() {
 | 
					// Creates a Taskbar Tab window and verifies UI elements match expectations.
 | 
				
			||||||
  let win = await openTaskbarTabWindow();
 | 
					add_task(async function testOpenWindowChrome() {
 | 
				
			||||||
 | 
					  const win = await openTaskbarTabWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  checkWindowChrome(win);
 | 
					  checkWindowChrome(win);
 | 
				
			||||||
  await checkHamburgerMenu(win);
 | 
					  await checkHamburgerMenu(win);
 | 
				
			||||||
  await BrowserTestUtils.closeWindow(win);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Simulate opening a taskbar tab window via
 | 
					 | 
				
			||||||
  // command line flag
 | 
					 | 
				
			||||||
  let cmdLine = Cu.createCommandLine(
 | 
					 | 
				
			||||||
    ["-taskbar-tab", "about:blank"],
 | 
					 | 
				
			||||||
    null,
 | 
					 | 
				
			||||||
    Ci.nsICommandLine.STATE_INITIAL_LAUNCH
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let newWinPromise = BrowserTestUtils.waitForNewWindow({
 | 
					 | 
				
			||||||
    url: "about:blank",
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let cmdLineHandler = Cc["@mozilla.org/browser/taskbar-tabs-clh;1"].getService(
 | 
					 | 
				
			||||||
    Ci.nsICommandLineHandler
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  cmdLineHandler.handle(cmdLine);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  win = await newWinPromise;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  checkWindowChrome(win);
 | 
					 | 
				
			||||||
  await checkHamburgerMenu(win);
 | 
					 | 
				
			||||||
  await BrowserTestUtils.closeWindow(win);
 | 
					  await BrowserTestUtils.closeWindow(win);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,199 @@
 | 
				
			||||||
 | 
					/* Any copyright is dedicated to the Public Domain.
 | 
				
			||||||
 | 
					http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChromeUtils.defineESModuleGetters(this, {
 | 
				
			||||||
 | 
					  TaskbarTabsUtils: "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs",
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					XPCOMUtils.defineLazyServiceGetters(this, {
 | 
				
			||||||
 | 
					  WinTaskbar: ["@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"],
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const registry = new TaskbarTabsRegistry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const url1 = Services.io.newURI("https://example.com");
 | 
				
			||||||
 | 
					const userContextId1 = 0;
 | 
				
			||||||
 | 
					const taskbarTab1 = registry.findOrCreateTaskbarTab(url1, userContextId1);
 | 
				
			||||||
 | 
					const id1 = taskbarTab1.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const url2 = Services.io.newURI("https://subdomain.example.com");
 | 
				
			||||||
 | 
					const userContextId2 = 1;
 | 
				
			||||||
 | 
					const taskbarTab2 = registry.findOrCreateTaskbarTab(url2, userContextId2);
 | 
				
			||||||
 | 
					const id2 = taskbarTab2.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_setup(async () => {
 | 
				
			||||||
 | 
					  await SpecialPowers.pushPrefEnv({
 | 
				
			||||||
 | 
					    set: [["network.dns.localDomains", [url1.host, url2.host]]],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_task(async function test_count_for_id() {
 | 
				
			||||||
 | 
					  const wm = new TaskbarTabsWindowManager();
 | 
				
			||||||
 | 
					  let testWindowCount = (aCount1, aCount2) => {
 | 
				
			||||||
 | 
					    is(
 | 
				
			||||||
 | 
					      wm.getCountForId(id1),
 | 
				
			||||||
 | 
					      aCount1,
 | 
				
			||||||
 | 
					      `${aCount1} Taskbar Tab window(s) should exist for id ${id1}`
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    is(
 | 
				
			||||||
 | 
					      wm.getCountForId(id2),
 | 
				
			||||||
 | 
					      aCount2,
 | 
				
			||||||
 | 
					      `${aCount2} Taskbar Tab window(s) should exist for id ${id2}`
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.openWindow(taskbarTab1);
 | 
				
			||||||
 | 
					  let win1_to_eject = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.openWindow(taskbarTab1);
 | 
				
			||||||
 | 
					  let win2 = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(2, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.openWindow(taskbarTab2);
 | 
				
			||||||
 | 
					  let win3_to_eject = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(2, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let tab1_adopted = await BrowserTestUtils.addTab(window.gBrowser, url1.spec);
 | 
				
			||||||
 | 
					  windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.replaceTabWithWindow(taskbarTab1, tab1_adopted);
 | 
				
			||||||
 | 
					  let win4 = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(3, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let tabOpenPromise = BrowserTestUtils.waitForEvent(
 | 
				
			||||||
 | 
					    window.gBrowser.tabContainer,
 | 
				
			||||||
 | 
					    "TabOpen"
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  await wm.ejectWindow(win1_to_eject);
 | 
				
			||||||
 | 
					  let tab2 = (await tabOpenPromise).target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(2, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tabOpenPromise = BrowserTestUtils.waitForEvent(
 | 
				
			||||||
 | 
					    window.gBrowser.tabContainer,
 | 
				
			||||||
 | 
					    "TabOpen"
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  await wm.ejectWindow(win3_to_eject);
 | 
				
			||||||
 | 
					  let tab3 = (await tabOpenPromise).target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  testWindowCount(2, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BrowserTestUtils.removeTab(tab2);
 | 
				
			||||||
 | 
					  BrowserTestUtils.removeTab(tab3);
 | 
				
			||||||
 | 
					  await Promise.all([
 | 
				
			||||||
 | 
					    BrowserTestUtils.closeWindow(win2),
 | 
				
			||||||
 | 
					    BrowserTestUtils.closeWindow(win4),
 | 
				
			||||||
 | 
					  ]);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_task(async function test_user_context_id() {
 | 
				
			||||||
 | 
					  function checkUserContextId(win, taskbarTab) {
 | 
				
			||||||
 | 
					    is(
 | 
				
			||||||
 | 
					      win.gBrowser.selectedTab.userContextId,
 | 
				
			||||||
 | 
					      taskbarTab.userContextId,
 | 
				
			||||||
 | 
					      "Tab's userContextId should match that for the Taskbar Tab."
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const wm = new TaskbarTabsWindowManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let testForTaskbarTab = async taskbarTab => {
 | 
				
			||||||
 | 
					    let windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					    await wm.openWindow(taskbarTab);
 | 
				
			||||||
 | 
					    let win = await windowPromise;
 | 
				
			||||||
 | 
					    checkUserContextId(win, taskbarTab);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tabOpenPromise = BrowserTestUtils.waitForEvent(
 | 
				
			||||||
 | 
					      window.gBrowser.tabContainer,
 | 
				
			||||||
 | 
					      "TabOpen"
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await wm.ejectWindow(win);
 | 
				
			||||||
 | 
					    let tab = (await tabOpenPromise).target;
 | 
				
			||||||
 | 
					    win = tab.ownerGlobal;
 | 
				
			||||||
 | 
					    checkUserContextId(win, taskbarTab);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					    await wm.replaceTabWithWindow(taskbarTab, tab);
 | 
				
			||||||
 | 
					    win = await windowPromise;
 | 
				
			||||||
 | 
					    checkUserContextId(win, taskbarTab);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await BrowserTestUtils.closeWindow(win);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await testForTaskbarTab(taskbarTab1);
 | 
				
			||||||
 | 
					  await testForTaskbarTab(taskbarTab2);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_task(async function test_eject_window_selected_tab() {
 | 
				
			||||||
 | 
					  const wm = new TaskbarTabsWindowManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.openWindow(taskbarTab1);
 | 
				
			||||||
 | 
					  let win = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const tabOpenPromise = BrowserTestUtils.waitForEvent(
 | 
				
			||||||
 | 
					    window.gBrowser.tabContainer,
 | 
				
			||||||
 | 
					    "TabOpen"
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  await wm.ejectWindow(win);
 | 
				
			||||||
 | 
					  let tab = (await tabOpenPromise).target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  is(
 | 
				
			||||||
 | 
					    tab,
 | 
				
			||||||
 | 
					    window.gBrowser.selectedTab,
 | 
				
			||||||
 | 
					    "The ejected Taskbar Tab should be the selected tab."
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await BrowserTestUtils.removeTab(tab);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_task(async function test_window_aumid() {
 | 
				
			||||||
 | 
					  const wm = new TaskbarTabsWindowManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.openWindow(taskbarTab1);
 | 
				
			||||||
 | 
					  let winOpen = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  is(
 | 
				
			||||||
 | 
					    TaskbarTabsUtils.getTaskbarTabIdFromWindow(winOpen),
 | 
				
			||||||
 | 
					    taskbarTab1.id,
 | 
				
			||||||
 | 
					    "The window's `tasbkartab` attribute should match the Taskbar Tab ID when opened."
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  is(
 | 
				
			||||||
 | 
					    WinTaskbar.getGroupIdForWindow(winOpen),
 | 
				
			||||||
 | 
					    taskbarTab1.id,
 | 
				
			||||||
 | 
					    "The window AUMID should match the Taskbar Tab ID when opened."
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let tab1_adopted = await BrowserTestUtils.addTab(window.gBrowser, url1.spec);
 | 
				
			||||||
 | 
					  windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
 | 
					  await wm.replaceTabWithWindow(taskbarTab1, tab1_adopted);
 | 
				
			||||||
 | 
					  let winReplace = await windowPromise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  is(
 | 
				
			||||||
 | 
					    TaskbarTabsUtils.getTaskbarTabIdFromWindow(winReplace),
 | 
				
			||||||
 | 
					    taskbarTab1.id,
 | 
				
			||||||
 | 
					    "The window's `tasbkartab` attribute should match the Taskbar Tab ID when a tab was replaced with a Tasbkar Tab window."
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					  is(
 | 
				
			||||||
 | 
					    WinTaskbar.getGroupIdForWindow(winReplace),
 | 
				
			||||||
 | 
					    taskbarTab1.id,
 | 
				
			||||||
 | 
					    "The window AUMID should match the Taskbar Tab ID when a tab was replaced with a Tasbkar Tab window."
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await Promise.all([
 | 
				
			||||||
 | 
					    BrowserTestUtils.closeWindow(winOpen),
 | 
				
			||||||
 | 
					    BrowserTestUtils.closeWindow(winReplace),
 | 
				
			||||||
 | 
					  ]);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -2,41 +2,39 @@
 | 
				
			||||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
					 * 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/. */
 | 
					 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChromeUtils.defineESModuleGetters(this, {
 | 
				
			||||||
 | 
					  TaskbarTabsRegistry:
 | 
				
			||||||
 | 
					    "resource:///modules/taskbartabs/TaskbarTabsRegistry.sys.mjs",
 | 
				
			||||||
 | 
					  TaskbarTabsWindowManager:
 | 
				
			||||||
 | 
					    "resource:///modules/taskbartabs/TaskbarTabsWindowManager.sys.mjs",
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Creates a web app window with the given tab,
 | 
					 * Creates a web app window with the given tab,
 | 
				
			||||||
 * then returns the window object for testing.
 | 
					 * then returns the window object for testing.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {Tab} tab
 | 
					 * @param {Tab} aTab
 | 
				
			||||||
 *        The tab that the web app should open with,
 | 
					 *        The tab that the web app should open with
 | 
				
			||||||
 *        about:blank will be opened if this value is null.
 | 
					 | 
				
			||||||
 * @returns {Promise}
 | 
					 * @returns {Promise}
 | 
				
			||||||
 *        The web app window object.
 | 
					 *        The web app window object.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function openTaskbarTabWindow(tab = null) {
 | 
					async function openTaskbarTabWindow(aTab = null) {
 | 
				
			||||||
  let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
 | 
					  const url = Services.io.newURI("https://example.com");
 | 
				
			||||||
    Ci.nsIWritablePropertyBag2
 | 
					  const userContextId = 0;
 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
  extraOptions.setPropertyAsBool("taskbartab", true);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 | 
					  const registry = new TaskbarTabsRegistry();
 | 
				
			||||||
 | 
					  const taskbarTab = registry.findOrCreateTaskbarTab(url, userContextId);
 | 
				
			||||||
 | 
					  const windowManager = new TaskbarTabsWindowManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  args.appendElement(tab);
 | 
					  const windowPromise = BrowserTestUtils.waitForNewWindow();
 | 
				
			||||||
  args.appendElement(extraOptions);
 | 
					 | 
				
			||||||
  args.appendElement(null);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Simulate opening a taskbar tab window
 | 
					  if (aTab) {
 | 
				
			||||||
  let win = Services.ww.openWindow(
 | 
					    windowManager.replaceTabWithWindow(taskbarTab, aTab);
 | 
				
			||||||
    null,
 | 
					  } else {
 | 
				
			||||||
    AppConstants.BROWSER_CHROME_URL,
 | 
					    windowManager.openWindow(taskbarTab);
 | 
				
			||||||
    "_blank",
 | 
					  }
 | 
				
			||||||
    "chrome,dialog=no,titlebar,close,toolbar,location,personalbar=no,status,menubar=no,resizable,minimizable,scrollbars",
 | 
					
 | 
				
			||||||
    args
 | 
					  return await windowPromise;
 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  await new Promise(resolve => {
 | 
					 | 
				
			||||||
    win.addEventListener("load", resolve, { once: true });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  await win.delayedStartupPromise;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return win;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -278,6 +278,9 @@ export const BrowserWindowTracker = {
 | 
				
			||||||
   *   True to make the window a private browsing window.
 | 
					   *   True to make the window a private browsing window.
 | 
				
			||||||
   * @param {String} [options.features]
 | 
					   * @param {String} [options.features]
 | 
				
			||||||
   *   Additional window features to give the new window.
 | 
					   *   Additional window features to give the new window.
 | 
				
			||||||
 | 
					   * @param {boolean} [options.all]
 | 
				
			||||||
 | 
					   *   True if "all" should be included as a window feature. If omitted, defaults
 | 
				
			||||||
 | 
					   *   to true.
 | 
				
			||||||
   * @param {nsIArray | nsISupportsString} [options.args]
 | 
					   * @param {nsIArray | nsISupportsString} [options.args]
 | 
				
			||||||
   *   Arguments to pass to the new window.
 | 
					   *   Arguments to pass to the new window.
 | 
				
			||||||
   * @param {boolean} [options.remote]
 | 
					   * @param {boolean} [options.remote]
 | 
				
			||||||
| 
						 | 
					@ -293,11 +296,15 @@ export const BrowserWindowTracker = {
 | 
				
			||||||
    openerWindow = undefined,
 | 
					    openerWindow = undefined,
 | 
				
			||||||
    private: isPrivate = false,
 | 
					    private: isPrivate = false,
 | 
				
			||||||
    features = undefined,
 | 
					    features = undefined,
 | 
				
			||||||
 | 
					    all = true,
 | 
				
			||||||
    args = null,
 | 
					    args = null,
 | 
				
			||||||
    remote = undefined,
 | 
					    remote = undefined,
 | 
				
			||||||
    fission = undefined,
 | 
					    fission = undefined,
 | 
				
			||||||
  } = {}) {
 | 
					  } = {}) {
 | 
				
			||||||
    let windowFeatures = "chrome,dialog=no,all";
 | 
					    let windowFeatures = "chrome,dialog=no";
 | 
				
			||||||
 | 
					    if (all) {
 | 
				
			||||||
 | 
					      windowFeatures += ",all";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (features) {
 | 
					    if (features) {
 | 
				
			||||||
      windowFeatures += `,${features}`;
 | 
					      windowFeatures += `,${features}`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue