TaskbarTabs.sys.mjs (5580B)
1 /* vim: se cin sw=2 ts=2 et filetype=javascript : 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * This file represents the entry point into the Taskbar Tabs system, 8 * initializing necessary subsystems before the export can be used. Code driving 9 * the Taskbar Tabs systems should interact with it through this interface. 10 */ 11 12 import { 13 TaskbarTabsRegistry, 14 TaskbarTabsRegistryStorage, 15 kTaskbarTabsRegistryEvents, 16 } from "resource:///modules/taskbartabs/TaskbarTabsRegistry.sys.mjs"; 17 import { TaskbarTabsWindowManager } from "resource:///modules/taskbartabs/TaskbarTabsWindowManager.sys.mjs"; 18 import { TaskbarTabsPin } from "resource:///modules/taskbartabs/TaskbarTabsPin.sys.mjs"; 19 import { TaskbarTabsUtils } from "resource:///modules/taskbartabs/TaskbarTabsUtils.sys.mjs"; 20 21 let lazy = {}; 22 23 ChromeUtils.defineESModuleGetters(lazy, { 24 ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs", 25 }); 26 27 ChromeUtils.defineLazyGetter(lazy, "logConsole", () => { 28 return console.createInstance({ 29 prefix: "TaskbarTabs", 30 maxLogLevel: "Warn", 31 }); 32 }); 33 34 /** 35 * A Taskbar Tabs singleton which ensures the system has been initialized before 36 * it can be interacted with. Methods on this object pass through to the Taskbar 37 * Tabs registry or window manager. 38 */ 39 export const TaskbarTabs = new (class { 40 #ready; 41 #registry; 42 #windowManager; 43 44 constructor() { 45 this.#ready = initRegistry().then(registry => { 46 this.#registry = registry; 47 this.#windowManager = initWindowManager(registry); 48 49 this.#updateMetrics(); 50 }); 51 } 52 53 #updateMetrics() { 54 Glean.webApp.installedWebAppCount.set(this.#registry.countTaskbarTabs()); 55 } 56 57 async waitUntilReady() { 58 await this.#ready; 59 } 60 61 async getTaskbarTab(...args) { 62 await this.#ready; 63 return this.#registry.getTaskbarTab(...args); 64 } 65 66 async findOrCreateTaskbarTab(...args) { 67 await this.#ready; 68 let result = this.#registry.findOrCreateTaskbarTab(...args); 69 70 if (result.created) { 71 this.#updateMetrics(); 72 73 // Don't wait for the pinning to complete. 74 TaskbarTabsPin.pinTaskbarTab(result.taskbarTab, this.#registry); 75 } 76 77 return result; 78 } 79 80 async findTaskbarTab(...args) { 81 await this.#ready; 82 return this.#registry.findTaskbarTab(...args); 83 } 84 85 /** 86 * Moves an existing tab into a new Taskbar Tab window. 87 * 88 * If there is already a Taskbar Tab for the tab's selected URL and container, 89 * opens the existing Taskbar Tab. If not, a new Taskbar Tab is created. 90 * 91 * @param {MozTabbrowserTab} aTab - The tab to move into a Taskbar Tab window. 92 * @returns {{window: DOMWindow, taskbarTab: TaskbarTab}} The created window 93 * and the Taskbar Tab it is associated with. 94 */ 95 async moveTabIntoTaskbarTab(aTab) { 96 const browser = aTab.linkedBrowser; 97 let url = browser.currentURI; 98 let userContextId = aTab.userContextId; 99 100 let [, manifest] = await Promise.all([ 101 this.#ready, 102 lazy.ManifestObtainer.browserObtainManifest(browser).catch(e => { 103 lazy.logConsole.error(e); 104 return {}; 105 }), 106 ]); 107 108 let { taskbarTab } = await this.findOrCreateTaskbarTab( 109 url, 110 userContextId, 111 // 'manifest' can be null if the site doesn't have a manifest. 112 manifest ? { manifest } : {} 113 ); 114 115 let win = await this.replaceTabWithWindow(taskbarTab, aTab); 116 return { 117 window: win, 118 taskbarTab, 119 }; 120 } 121 122 async resetForTests(...args) { 123 await this.#ready; 124 return this.#registry.resetForTests(...args); 125 } 126 127 async removeTaskbarTab(...args) { 128 await this.#ready; 129 130 let taskbarTab = this.#registry.removeTaskbarTab(...args); 131 this.#updateMetrics(); 132 133 // Don't wait for unpinning to finish. 134 TaskbarTabsPin.unpinTaskbarTab(taskbarTab, this.#registry); 135 } 136 137 async openWindow(...args) { 138 await this.#ready; 139 return this.#windowManager.openWindow(...args); 140 } 141 142 async replaceTabWithWindow(...args) { 143 await this.#ready; 144 return this.#windowManager.replaceTabWithWindow(...args); 145 } 146 147 async ejectWindow(...args) { 148 await this.#ready; 149 return this.#windowManager.ejectWindow(...args); 150 } 151 152 async getCountForId(...args) { 153 await this.#ready; 154 return this.#windowManager.getCountForId(...args); 155 } 156 })(); 157 158 /** 159 * Taskbar Tabs Registry initialization. 160 * 161 * @returns {TaskbarTabsRegistry} A registry after loading and hooking saving to persistent storage. 162 */ 163 async function initRegistry() { 164 const kRegistryFilename = "taskbartabs.json"; 165 // Construct the path [Profile]/taskbartabs/taskbartabs.json. 166 let registryFile = TaskbarTabsUtils.getTaskbarTabsFolder(); 167 registryFile.append(kRegistryFilename); 168 169 let init = {}; 170 if (registryFile.exists()) { 171 init.loadFile = registryFile; 172 } 173 174 let registry = await TaskbarTabsRegistry.create(init); 175 176 // Initialize persistent storage. 177 let storage = new TaskbarTabsRegistryStorage(registry, registryFile); 178 registry.on(kTaskbarTabsRegistryEvents.created, () => { 179 storage.save(); 180 }); 181 registry.on(kTaskbarTabsRegistryEvents.patched, () => { 182 storage.save(); 183 }); 184 registry.on(kTaskbarTabsRegistryEvents.removed, () => { 185 storage.save(); 186 }); 187 188 return registry; 189 } 190 191 /** 192 * Taskbar Tabs Window Manager initialization. 193 * 194 * @returns {TaskbarTabsWindowManager} The initialized Window Manager 195 */ 196 function initWindowManager() { 197 let wm = new TaskbarTabsWindowManager(); 198 199 return wm; 200 }