diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /mobile/android/modules/geckoview/GeckoViewTab.sys.mjs | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/modules/geckoview/GeckoViewTab.sys.mjs')
-rw-r--r-- | mobile/android/modules/geckoview/GeckoViewTab.sys.mjs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/mobile/android/modules/geckoview/GeckoViewTab.sys.mjs b/mobile/android/modules/geckoview/GeckoViewTab.sys.mjs new file mode 100644 index 0000000000..53f43f153c --- /dev/null +++ b/mobile/android/modules/geckoview/GeckoViewTab.sys.mjs @@ -0,0 +1,219 @@ +/* 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 { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs"; + +import { ExtensionUtils } from "resource://gre/modules/ExtensionUtils.sys.mjs"; + +const { ExtensionError } = ExtensionUtils; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", + mobileWindowTracker: "resource://gre/modules/GeckoViewWebExtension.sys.mjs", +}); + +class Tab { + constructor(window) { + this.id = GeckoViewTabBridge.windowIdToTabId(window.docShell.outerWindowID); + this.browser = window.browser; + this.active = false; + } + + get linkedBrowser() { + return this.browser; + } + + getActive() { + return this.active; + } + + get userContextId() { + return this.browser.ownerGlobal.moduleManager.settings + .unsafeSessionContextId; + } +} + +// Because of bug 1410749, we can't use 0, though, and just to be safe +// we choose a value that is unlikely to overlap with Fennec's tab IDs. +const TAB_ID_BASE = 10000; + +export const GeckoViewTabBridge = { + /** + * Converts windowId to tabId as in GeckoView every browser window has exactly one tab. + * + * @param {number} windowId outerWindowId + * + * @returns {number} tabId + */ + windowIdToTabId(windowId) { + return TAB_ID_BASE + windowId; + }, + + /** + * Converts tabId to windowId. + * + * @param {number} tabId + * + * @returns {number} + * outerWindowId of browser window to which the tab belongs. + */ + tabIdToWindowId(tabId) { + return tabId - TAB_ID_BASE; + }, + + /** + * Delegates openOptionsPage handling to the app. + * + * @param {number} extensionId + * The ID of the extension requesting the options menu. + * + * @returns {Promise<Void>} + * A promise resolved after successful handling. + */ + async openOptionsPage(extensionId) { + debug`openOptionsPage for extensionId ${extensionId}`; + + try { + await lazy.EventDispatcher.instance.sendRequestForResult({ + type: "GeckoView:WebExtension:OpenOptionsPage", + extensionId, + }); + } catch (errorMessage) { + // The error message coming from GeckoView is about :OpenOptionsPage not + // being registered so we need to have one that's extension friendly + // here. + throw new ExtensionError("runtime.openOptionsPage is not supported"); + } + }, + + /** + * Request the GeckoView App to create a new tab (GeckoSession). + * + * @param {object} options + * @param {string} options.extensionId + * The ID of the extension that requested a new tab. + * @param {object} options.createProperties + * The properties for the new tab, see tabs.create reference for details. + * + * @returns {Promise<Tab>} + * A promise resolved to the newly created tab. + * @throws {Error} + * Throws an error if the GeckoView app doesn't support tabs.create or fails to handle the request. + */ + async createNewTab({ extensionId, createProperties } = {}) { + debug`createNewTab`; + + const newSessionId = Services.uuid + .generateUUID() + .toString() + .slice(1, -1) + .replace(/-/g, ""); + + // The window might already be open by the time we get the response, so we + // need to start waiting before we send the message. + const windowPromise = new Promise(resolve => { + const handler = { + observe(aSubject, aTopic, aData) { + if ( + aTopic === "geckoview-window-created" && + aSubject.name === newSessionId + ) { + Services.obs.removeObserver(handler, "geckoview-window-created"); + resolve(aSubject); + } + }, + }; + Services.obs.addObserver(handler, "geckoview-window-created"); + }); + + let didOpenSession = false; + try { + didOpenSession = await lazy.EventDispatcher.instance.sendRequestForResult( + { + type: "GeckoView:WebExtension:NewTab", + extensionId, + createProperties, + newSessionId, + } + ); + } catch (errorMessage) { + // The error message coming from GeckoView is about :NewTab not being + // registered so we need to have one that's extension friendly here. + throw new ExtensionError("tabs.create is not supported"); + } + + if (!didOpenSession) { + throw new ExtensionError("Cannot create new tab"); + } + + const window = await windowPromise; + if (!window.tab) { + window.tab = new Tab(window); + } + return window.tab; + }, + + /** + * Request the GeckoView App to close a tab (GeckoSession). + * + * + * @param {object} options + * @param {Window} options.window The window owning the tab to close + * @param {string} options.extensionId + * + * @returns {Promise<Void>} + * A promise resolved after GeckoSession is closed. + * @throws {Error} + * Throws an error if the GeckoView app doesn't allow extension to close tab. + */ + async closeTab({ window, extensionId } = {}) { + try { + await window.WindowEventDispatcher.sendRequestForResult({ + type: "GeckoView:WebExtension:CloseTab", + extensionId, + }); + } catch (errorMessage) { + throw new ExtensionError(errorMessage); + } + }, + + async updateTab({ window, extensionId, updateProperties } = {}) { + try { + await window.WindowEventDispatcher.sendRequestForResult({ + type: "GeckoView:WebExtension:UpdateTab", + extensionId, + updateProperties, + }); + } catch (errorMessage) { + throw new ExtensionError(errorMessage); + } + }, +}; + +export class GeckoViewTab extends GeckoViewModule { + onInit() { + const { window } = this; + if (!window.tab) { + window.tab = new Tab(window); + } + + this.registerListener(["GeckoView:WebExtension:SetTabActive"]); + } + + onEvent(aEvent, aData, aCallback) { + debug`onEvent: event=${aEvent}, data=${aData}`; + + switch (aEvent) { + case "GeckoView:WebExtension:SetTabActive": { + const { active } = aData; + lazy.mobileWindowTracker.setTabActive(this.window, active); + break; + } + } + } +} + +const { debug, warn } = GeckoViewTab.initLogging("GeckoViewTab"); |