1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
9 EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
10 MobileTabBrowser: "chrome://remote/content/shared/MobileTabBrowser.sys.mjs",
13 // Maps browser's permanentKey to uuid: WeakMap.<Object, string>
14 const browserUniqueIds = new WeakMap();
16 export var TabManager = {
18 * Retrieve all the browser elements from tabs as contained in open windows.
20 * @return {Array<xul:browser>}
21 * All the found <xul:browser>s. Will return an empty array if
22 * no windows and tabs can be found.
27 for (const win of this.windows) {
28 const tabBrowser = this.getTabBrowser(win);
30 if (tabBrowser && tabBrowser.tabs) {
31 const contentBrowsers = tabBrowser.tabs.map(tab => {
32 return this.getBrowserForTab(tab);
34 browsers.push(...contentBrowsers);
42 return Services.wm.getEnumerator(null);
46 * Array of unique browser ids (UUIDs) for all content browsers of all
49 * TODO: Similarly to getBrowserById, we should improve the performance of
50 * this getter in Bug 1750065.
52 * @return {Array<String>}
53 * Array of UUIDs for all content browsers.
55 get allBrowserUniqueIds() {
56 const browserIds = [];
58 for (const win of this.windows) {
59 const tabBrowser = this.getTabBrowser(win);
61 // Only return handles for browser windows
62 if (tabBrowser && tabBrowser.tabs) {
63 for (const tab of tabBrowser.tabs) {
64 const contentBrowser = this.getBrowserForTab(tab);
65 const winId = this.getIdForBrowser(contentBrowser);
67 browserIds.push(winId);
77 * Get the <code><xul:browser></code> for the specified tab.
80 * The tab whose browser needs to be returned.
82 * @return {xul:browser}
83 * The linked browser for the tab or null if no browser can be found.
85 getBrowserForTab(tab) {
86 if (tab && "linkedBrowser" in tab) {
87 return tab.linkedBrowser;
94 * Return the tab browser for the specified chrome window.
96 * @param {ChromeWindow} win
97 * Window whose <code>tabbrowser</code> needs to be accessed.
100 * Tab browser or null if it's not a browser window.
103 if (lazy.AppInfo.isAndroid) {
104 return new lazy.MobileTabBrowser(win);
105 } else if (lazy.AppInfo.isFirefox) {
115 * @param {Object} options
116 * @param {Boolean=} options.focus
117 * Set to true if the new tab should be focused (selected). Defaults to
119 * @param {Number} options.userContextId
120 * The user context (container) id.
121 * @param {window=} options.window
122 * The window where the new tab will open. Defaults to Services.wm.getMostRecentWindow
123 * if no window is provided.
125 async addTab(options = {}) {
129 window = Services.wm.getMostRecentWindow(null),
131 const tabBrowser = this.getTabBrowser(window);
133 const tab = await tabBrowser.addTab("about:blank", {
134 triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
139 await this.selectTab(tab);
146 * Retrieve the browser element corresponding to the provided unique id,
147 * previously generated via getIdForBrowser.
149 * TODO: To avoid creating strong references on browser elements and
150 * potentially leaking those elements, this method loops over all windows and
151 * all tabs. It should be replaced by a faster implementation in Bug 1750065.
154 * A browser unique id created by getIdForBrowser.
155 * @return {xul:browser}
156 * The <xul:browser> corresponding to the provided id. Will return null if
157 * no matching browser element is found.
160 for (const win of this.windows) {
161 const tabBrowser = this.getTabBrowser(win);
162 if (tabBrowser && tabBrowser.tabs) {
163 for (let i = 0; i < tabBrowser.tabs.length; ++i) {
164 const contentBrowser = this.getBrowserForTab(tabBrowser.tabs[i]);
165 if (this.getIdForBrowser(contentBrowser) == id) {
166 return contentBrowser;
175 * Retrieve the browsing context corresponding to the provided unique id.
178 * A browsing context unique id (created by getIdForBrowsingContext).
179 * @return {BrowsingContext=}
180 * The browsing context found for this id, null if none was found.
182 getBrowsingContextById(id) {
183 const browser = this.getBrowserById(id);
185 return browser.browsingContext;
188 return BrowsingContext.get(id);
192 * Retrieve the unique id for the given xul browser element. The id is a
193 * dynamically generated uuid associated with the permanentKey property of the
194 * given browser element. This method is preferable over getIdForBrowsingContext
195 * in case of working with browser element of a tab, since we can not guarantee
196 * that browsing context is attached to it.
198 * @param {xul:browser} browserElement
199 * The <xul:browser> for which we want to retrieve the id.
200 * @return {String} The unique id for this browser.
202 getIdForBrowser(browserElement) {
203 if (browserElement === null) {
207 const key = browserElement.permanentKey;
208 if (!browserUniqueIds.has(key)) {
209 const uuid = Services.uuid.generateUUID().toString();
210 browserUniqueIds.set(key, uuid.substring(1, uuid.length - 1));
212 return browserUniqueIds.get(key);
216 * Retrieve the id of a Browsing Context.
218 * For a top-level browsing context a custom unique id will be returned.
220 * @param {BrowsingContext=} browsingContext
221 * The browsing context to get the id from.
224 * The id of the browsing context.
226 getIdForBrowsingContext(browsingContext) {
227 if (!browsingContext) {
231 if (!browsingContext.parent) {
232 // Top-level browsing contexts have their own custom unique id.
233 return this.getIdForBrowser(browsingContext.embedderElement);
236 return browsingContext.id.toString();
241 for (const win of this.windows) {
242 // For browser windows count the tabs. Otherwise take the window itself.
243 const tabbrowser = this.getTabBrowser(win);
244 if (tabbrowser?.tabs) {
245 count += tabbrowser.tabs.length;
254 * Remove the given tab.
259 async removeTab(tab) {
260 const ownerWindow = this._getWindowForTab(tab);
261 const tabBrowser = this.getTabBrowser(ownerWindow);
262 await tabBrowser.removeTab(tab);
266 * Select the given tab.
272 * Promise that resolves when the given tab has been selected.
275 const ownerWindow = this._getWindowForTab(tab);
276 const tabBrowser = this.getTabBrowser(ownerWindow);
278 if (tab === tabBrowser.selectedTab) {
279 return Promise.resolve();
282 const selected = new lazy.EventPromise(ownerWindow, "TabSelect");
283 tabBrowser.selectedTab = tab;
288 return lazy.AppInfo.isAndroid || lazy.AppInfo.isFirefox;
291 _getWindowForTab(tab) {
292 // `.linkedBrowser.ownerGlobal` works both with Firefox Desktop and Mobile.
293 // Other accessors (eg `.ownerGlobal` or `.browser.ownerGlobal`) fail on one
295 return tab.linkedBrowser.ownerGlobal;