/* 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/. */ /** * This file represents the entry point into the Taskbar Tabs system, * initializing necessary subsystems before the export can be used. Code driving * the Taskbar Tabs systems should interact with it through this interface. */ import { TaskbarTabsRegistry, TaskbarTabsRegistryStorage, kTaskbarTabsRegistryEvents, } from "resource:///modules/taskbartabs/TaskbarTabsRegistry.sys.mjs"; import { TaskbarTabsWindowManager } from "resource:///modules/taskbartabs/TaskbarTabsWindowManager.sys.mjs"; import { TaskbarTabsPin } from "resource:///modules/taskbartabs/TaskbarTabsPin.sys.mjs"; import { TaskbarTabsUtils } from "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs"; let lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs", }); ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { return console.createInstance({ prefix: "TaskbarTabs", maxLogLevel: "Warn", }); }); /** * A Taskbar Tabs singleton which ensures the system has been initialized before * it can be interacted with. Methods on this object pass through to the Taskbar * Tabs registry or window manager. */ export const TaskbarTabs = new (class { #ready; #registry; #windowManager; constructor() { this.#ready = initRegistry().then(registry => { this.#registry = registry; this.#windowManager = initWindowManager(registry); initPinManager(registry); this.#setupTelemetry(registry); }); } #setupTelemetry(aRegistry) { function updateMetrics() { Glean.webApp.installedWebAppCount.set(aRegistry.countTaskbarTabs()); } aRegistry.on(kTaskbarTabsRegistryEvents.created, updateMetrics); aRegistry.on(kTaskbarTabsRegistryEvents.removed, updateMetrics); updateMetrics(); } async waitUntilReady() { await this.#ready; } async getTaskbarTab(...args) { await this.#ready; return this.#registry.getTaskbarTab(...args); } async findOrCreateTaskbarTab(...args) { await this.#ready; return this.#registry.findOrCreateTaskbarTab(...args); } async findTaskbarTab(...args) { await this.#ready; return this.#registry.findTaskbarTab(...args); } /** * Moves an existing tab into a new Taskbar Tab window. * * If there is already a Taskbar Tab for the tab's selected URL and container, * opens the existing Taskbar Tab. If not, a new Taskbar Tab is created. * * @param {MozTabbrowserTab} aTab - The tab to move into a Taskbar Tab window. * @returns {{window: DOMWindow, taskbarTab: TaskbarTab}} The created window * and the Taskbar Tab it is associated with. */ async moveTabIntoTaskbarTab(aTab) { const browser = aTab.linkedBrowser; let url = browser.currentURI; let userContextId = aTab.userContextId; let [, manifest] = await Promise.all([ this.#ready, lazy.ManifestObtainer.browserObtainManifest(browser).catch(e => { lazy.logConsole.error(e); return {}; }), ]); let taskbarTab = await this.findOrCreateTaskbarTab( url, userContextId, // 'manifest' can be null if the site doesn't have a manifest. manifest ? { manifest } : {} ); let win = await this.replaceTabWithWindow(taskbarTab, aTab); return { window: win, taskbarTab, }; } async resetForTests(...args) { await this.#ready; return this.#registry.resetForTests(...args); } async removeTaskbarTab(...args) { await this.#ready; return this.#registry.removeTaskbarTab(...args); } async openWindow(...args) { await this.#ready; return this.#windowManager.openWindow(...args); } async replaceTabWithWindow(...args) { await this.#ready; return this.#windowManager.replaceTabWithWindow(...args); } async ejectWindow(...args) { await this.#ready; return this.#windowManager.ejectWindow(...args); } async getCountForId(...args) { await this.#ready; return this.#windowManager.getCountForId(...args); } })(); /** * Taskbar Tabs Registry initialization. * * @returns {TaskbarTabsRegistry} A registry after loading and hooking saving to persistent storage. */ async function initRegistry() { const kRegistryFilename = "taskbartabs.json"; // Construct the path [Profile]/taskbartabs/taskbartabs.json. let registryFile = TaskbarTabsUtils.getTaskbarTabsFolder(); registryFile.append(kRegistryFilename); let init = {}; if (registryFile.exists()) { init.loadFile = registryFile; } let registry = await TaskbarTabsRegistry.create(init); // Initialize persistent storage. let storage = new TaskbarTabsRegistryStorage(registry, registryFile); registry.on(kTaskbarTabsRegistryEvents.created, () => { storage.save(); }); registry.on(kTaskbarTabsRegistryEvents.patched, () => { storage.save(); }); registry.on(kTaskbarTabsRegistryEvents.removed, () => { storage.save(); }); return registry; } /** * Taskbar Tabs Window Manager initialization. * * @returns {TaskbarTabsWindowManager} The initialized Window Manager */ function initWindowManager() { let wm = new TaskbarTabsWindowManager(); return wm; } /** * Taskbar Tabs Pinning initialization. * * @param {TaskbarTabsRegistry} aRegistry - A registry to drive events to trigger pinning. */ function initPinManager(aRegistry) { aRegistry.on(kTaskbarTabsRegistryEvents.created, (_, taskbarTab) => { return TaskbarTabsPin.pinTaskbarTab(taskbarTab, aRegistry); }); aRegistry.on(kTaskbarTabsRegistryEvents.removed, (_, taskbarTab) => { return TaskbarTabsPin.unpinTaskbarTab(taskbarTab, aRegistry); }); }