diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/platform')
4 files changed, 177 insertions, 234 deletions
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index 40993477b..f87500c4f 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -17,6 +17,10 @@ import { CoreApiResponse, NotificationType } from "@gnu-taler/taler-util"; import { WalletOperations } from "@gnu-taler/taler-wallet-core"; import { BackgroundOperations } from "../wxApi.js"; +import { + ExtensionOperations, + MessageFromExtension, +} from "../taler-wallet-interaction-loader.js"; export interface Permissions { /** @@ -35,9 +39,9 @@ export interface Permissions { * Compatibility API that works on multiple browsers. */ export interface CrossBrowserPermissionsApi { - containsHostPermissions(): Promise<boolean>; - requestHostPermissions(): Promise<boolean>; - removeHostPermissions(): Promise<boolean>; + // containsHostPermissions(): Promise<boolean>; + // requestHostPermissions(): Promise<boolean>; + // removeHostPermissions(): Promise<boolean>; containsClipboardPermissions(): Promise<boolean>; requestClipboardPermissions(): Promise<boolean>; @@ -53,9 +57,11 @@ export type MessageFromBackend = { }; export type MessageFromFrontend< - Op extends BackgroundOperations | WalletOperations, + Op extends BackgroundOperations | WalletOperations | ExtensionOperations, > = Op extends BackgroundOperations ? MessageFromFrontendBackground<keyof BackgroundOperations> + : Op extends ExtensionOperations + ? MessageFromExtension<keyof ExtensionOperations> : Op extends WalletOperations ? MessageFromFrontendWallet<keyof WalletOperations> : never; @@ -81,12 +87,24 @@ export interface WalletWebExVersion { version: string; } +export interface Settings { + injectTalerSupport: boolean; +} + +export const defaultSettings: Settings = { + injectTalerSupport: false, +}; + /** * Compatibility helpers needed for browsers that don't implement * WebExtension APIs consistently. */ export interface BackgroundPlatformAPI { /** + * + */ + getSettingsFromStorage(): Promise<Settings>; + /** * Guarantee that the service workers don't die */ keepAlive(cb: VoidFunction): void; @@ -116,17 +134,13 @@ export interface BackgroundPlatformAPI { * Register a callback to be called when the wallet is ready to start * @param callback */ - notifyWhenAppIsReady(callback: () => void): void; + notifyWhenAppIsReady(): Promise<void>; /** * Get the wallet version from manifest */ getWalletWebExVersion(): WalletWebExVersion; /** - * Frontend API - */ - containsTalerHeaderListener(): boolean; - /** * Backend API */ registerAllIncomingConnections(): void; @@ -134,12 +148,6 @@ export interface BackgroundPlatformAPI { * Backend API */ registerReloadOnNewVersion(): void; - /** - * Backend API - */ - registerTalerHeaderListener( - onHeader: (tabId: number, url: string) => void, - ): void; /** * Permission API for checking and add a listener diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts index 03259314e..51cf2f64e 100644 --- a/packages/taler-wallet-webextension/src/platform/chrome.ts +++ b/packages/taler-wallet-webextension/src/platform/chrome.ts @@ -31,10 +31,13 @@ import { MessageFromFrontend, MessageResponse, Permissions, + Settings, + defaultSettings, } from "./api.js"; const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { isFirefox, + getSettingsFromStorage, findTalerUriInActiveTab, findTalerUriInClipboard, getPermissionsApi, @@ -49,11 +52,9 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { registerOnInstalled, listenToAllChannels: listenToAllChannels as any, registerReloadOnNewVersion, - registerTalerHeaderListener, sendMessageToAllChannels, sendMessageToBackground, useServiceWorkerAsBackgroundProcess, - containsTalerHeaderListener, keepAlive, }; @@ -61,6 +62,19 @@ export default api; const logger = new Logger("chrome.ts"); +async function getSettingsFromStorage(): Promise<Settings> { + const data = await chrome.storage.local.get("wallet-settings"); + if (!data) return defaultSettings; + const settings = data["wallet-settings"]; + if (!settings) return defaultSettings; + try { + const parsed = JSON.parse(settings); + return parsed; + } catch (e) { + return defaultSettings; + } +} + function keepAlive(callback: any): void { if (extensionIsManifestV3()) { chrome.alarms.create("wallet-worker", { periodInMinutes: 1 }); @@ -78,10 +92,10 @@ function isFirefox(): boolean { return false; } -const hostPermissions = { - permissions: ["webRequest"], - origins: ["http://*/*", "https://*/*"], -}; +// const hostPermissions = { +// permissions: ["webRequest"], +// origins: ["http://*/*", "https://*/*"], +// }; export function containsClipboardPermissions(): Promise<boolean> { return new Promise((res, rej) => { @@ -96,17 +110,17 @@ export function containsClipboardPermissions(): Promise<boolean> { }); } -export function containsHostPermissions(): Promise<boolean> { - return new Promise((res, rej) => { - chrome.permissions.contains(hostPermissions, (resp) => { - const le = chrome.runtime.lastError?.message; - if (le) { - rej(le); - } - res(resp); - }); - }); -} +// export function containsHostPermissions(): Promise<boolean> { +// return new Promise((res, rej) => { +// chrome.permissions.contains(hostPermissions, (resp) => { +// const le = chrome.runtime.lastError?.message; +// if (le) { +// rej(le); +// } +// res(resp); +// }); +// }); +// } export async function requestClipboardPermissions(): Promise<boolean> { return new Promise((res, rej) => { @@ -121,73 +135,67 @@ export async function requestClipboardPermissions(): Promise<boolean> { }); } -export async function requestHostPermissions(): Promise<boolean> { - return new Promise((res, rej) => { - chrome.permissions.request(hostPermissions, (resp) => { - const le = chrome.runtime.lastError?.message; - if (le) { - rej(le); - } - res(resp); - }); - }); -} - -type HeaderListenerFunc = ( - details: chrome.webRequest.WebResponseHeadersDetails, -) => void; -let currentHeaderListener: HeaderListenerFunc | undefined = undefined; - -type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void; -let currentTabListener: TabListenerFunc | undefined = undefined; - -export function containsTalerHeaderListener(): boolean { - return ( - currentHeaderListener !== undefined || currentTabListener !== undefined - ); -} - -export async function removeHostPermissions(): Promise<boolean> { - //if there is a handler already, remove it - if ( - currentHeaderListener && - chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener) - ) { - chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener); - } - if ( - currentTabListener && - chrome?.tabs?.onUpdated?.hasListener(currentTabListener) - ) { - chrome.tabs.onUpdated.removeListener(currentTabListener); - } - - currentHeaderListener = undefined; - currentTabListener = undefined; - - //notify the browser about this change, this operation is expensive - if ("webRequest" in chrome) { - chrome.webRequest.handlerBehaviorChanged(() => { - if (chrome.runtime.lastError) { - logger.error(JSON.stringify(chrome.runtime.lastError)); - } - }); - } - - if (extensionIsManifestV3()) { - // Trying to remove host permissions with manifest >= v3 throws an error - return true; - } - return new Promise((res, rej) => { - chrome.permissions.remove(hostPermissions, (resp) => { - const le = chrome.runtime.lastError?.message; - if (le) { - rej(le); - } - res(resp); - }); - }); -} +// export async function requestHostPermissions(): Promise<boolean> { +// return new Promise((res, rej) => { +// chrome.permissions.request(hostPermissions, (resp) => { +// const le = chrome.runtime.lastError?.message; +// if (le) { +// rej(le); +// } +// res(resp); +// }); +// }); +// } + +// type HeaderListenerFunc = ( +// details: chrome.webRequest.WebResponseHeadersDetails, +// ) => void; +// let currentHeaderListener: HeaderListenerFunc | undefined = undefined; + +// type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void; +// let currentTabListener: TabListenerFunc | undefined = undefined; + +// export async function removeHostPermissions(): Promise<boolean> { +// //if there is a handler already, remove it +// if ( +// currentHeaderListener && +// chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener) +// ) { +// chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener); +// } +// if ( +// currentTabListener && +// chrome?.tabs?.onUpdated?.hasListener(currentTabListener) +// ) { +// chrome.tabs.onUpdated.removeListener(currentTabListener); +// } + +// currentHeaderListener = undefined; +// currentTabListener = undefined; + +// //notify the browser about this change, this operation is expensive +// if ("webRequest" in chrome) { +// chrome.webRequest.handlerBehaviorChanged(() => { +// if (chrome.runtime.lastError) { +// logger.error(JSON.stringify(chrome.runtime.lastError)); +// } +// }); +// } + +// if (extensionIsManifestV3()) { +// // Trying to remove host permissions with manifest >= v3 throws an error +// return true; +// } +// return new Promise((res, rej) => { +// chrome.permissions.remove(hostPermissions, (resp) => { +// const le = chrome.runtime.lastError?.message; +// if (le) { +// rej(le); +// } +// res(resp); +// }); +// }); +// } export function removeClipboardPermissions(): Promise<boolean> { return new Promise((res, rej) => { @@ -214,9 +222,9 @@ function addPermissionsListener( function getPermissionsApi(): CrossBrowserPermissionsApi { return { addPermissionsListener, - containsHostPermissions, - requestHostPermissions, - removeHostPermissions, + // containsHostPermissions, + // requestHostPermissions, + // removeHostPermissions, requestClipboardPermissions, removeClipboardPermissions, containsClipboardPermissions, @@ -227,12 +235,16 @@ function getPermissionsApi(): CrossBrowserPermissionsApi { * * @param callback function to be called */ -function notifyWhenAppIsReady(callback: () => void): void { - if (extensionIsManifestV3()) { - callback(); - } else { - window.addEventListener("load", callback); - } +function notifyWhenAppIsReady(): Promise<void> { + return new Promise((resolve, reject) => { + if (extensionIsManifestV3()) { + resolve(); + } else { + window.addEventListener("load", () => { + resolve(); + }); + } + }); } function openWalletURIFromPopup(maybeTalerUri: string): void { @@ -478,101 +490,6 @@ function getWalletWebExVersion(): WalletVersion { return manifestData; } -function registerTalerHeaderListener( - callback: (tabId: number, url: string) => void, -): void { - logger.trace("setting up header listener"); - - function headerListener( - details: chrome.webRequest.WebResponseHeadersDetails, - ): void { - if (chrome.runtime.lastError) { - logger.error(JSON.stringify(chrome.runtime.lastError)); - return; - } - if ( - details.statusCode === 402 || - details.statusCode === 202 || - details.statusCode === 200 - ) { - const values = (details.responseHeaders || []) - .filter((h) => h.name.toLowerCase() === "taler") - .map((h) => h.value) - .filter((value): value is string => !!value); - if (values.length > 0) { - logger.info( - `Found a Taler URI in a response header for the request ${details.url} from tab ${details.tabId}`, - ); - callback(details.tabId, values[0]); - } - } - return; - } - - async function tabListener( - tabId: number, - info: chrome.tabs.TabChangeInfo, - ): Promise<void> { - if (tabId < 0) return; - const tabLocationHasBeenUpdated = info.status === "complete"; - const tabTitleHasBeenUpdated = info.title !== undefined; - if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) { - const uri = await findTalerUriInTab(tabId); - if (!uri) return; - logger.info(`Found a Taler URI in the tab ${tabId}`); - callback(tabId, uri); - } - } - - const prevHeaderListener = currentHeaderListener; - const prevTabListener = currentTabListener; - - getPermissionsApi() - .containsHostPermissions() - .then((result) => { - //if there is a handler already, remove it - if ( - prevHeaderListener && - chrome?.webRequest?.onHeadersReceived?.hasListener(prevHeaderListener) - ) { - chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener); - } - if ( - prevTabListener && - chrome?.tabs?.onUpdated?.hasListener(prevTabListener) - ) { - chrome.tabs.onUpdated.removeListener(prevTabListener); - } - - //if the result was positive, add the headerListener - if (result) { - const headersEvent: - | chrome.webRequest.WebResponseHeadersEvent - | undefined = chrome?.webRequest?.onHeadersReceived; - if (headersEvent) { - headersEvent.addListener(headerListener, { urls: ["<all_urls>"] }, [ - "responseHeaders", - ]); - currentHeaderListener = headerListener; - } - - const tabsEvent: chrome.tabs.TabUpdatedEvent | undefined = - chrome?.tabs?.onUpdated; - if (tabsEvent) { - tabsEvent.addListener(tabListener); - currentTabListener = tabListener; - } - } - - //notify the browser about this change, this operation is expensive - chrome?.webRequest?.handlerBehaviorChanged(() => { - if (chrome.runtime.lastError) { - logger.error(JSON.stringify(chrome.runtime.lastError)); - } - }); - }); -} - const alertIcons = { "16": "/static/img/taler-alert-16.png", "19": "/static/img/taler-alert-19.png", @@ -750,7 +667,7 @@ function registerOnInstalled(callback: () => void): void { if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) { callback(); } - registerIconChangeOnTalerContent(); + await registerIconChangeOnTalerContent(); }); } diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts index d57072c80..1a4183bec 100644 --- a/packages/taler-wallet-webextension/src/platform/dev.ts +++ b/packages/taler-wallet-webextension/src/platform/dev.ts @@ -23,18 +23,17 @@ import { MessageFromBackend, MessageFromFrontend, MessageResponse, + defaultSettings, } from "./api.js"; const frames = ["popup", "wallet"]; const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { isFirefox: () => false, + getSettingsFromStorage: () => Promise.resolve(defaultSettings), keepAlive: (cb: VoidFunction) => cb(), findTalerUriInActiveTab: async () => undefined, findTalerUriInClipboard: async () => undefined, - containsTalerHeaderListener: () => { - return true; - }, getPermissionsApi: () => ({ addPermissionsListener: () => undefined, containsHostPermissions: async () => true, @@ -47,21 +46,23 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { getWalletWebExVersion: () => ({ version: "none", }), - notifyWhenAppIsReady: (fn: () => void) => { + notifyWhenAppIsReady: () => { let total = frames.length; - function waitAndNotify(): void { - total--; - if (total < 1) { - fn(); - } - } - frames.forEach((f) => { - const theFrame = window.frames[f as any]; - if (theFrame.location.href === "about:blank") { - waitAndNotify(); - } else { - theFrame.addEventListener("load", waitAndNotify); + return new Promise((fn) => { + function waitAndNotify(): void { + total--; + if (total < 1) { + fn(); + } } + frames.forEach((f) => { + const theFrame = window.frames[f as any]; + if (theFrame.location.href === "about:blank") { + waitAndNotify(); + } else { + theFrame.addEventListener("load", waitAndNotify); + } + }); }); }, @@ -80,9 +81,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { }, registerAllIncomingConnections: () => undefined, - registerOnInstalled: (fn: () => void) => undefined, + registerOnInstalled: () => Promise.resolve(), registerReloadOnNewVersion: () => undefined, - registerTalerHeaderListener: () => undefined, useServiceWorkerAsBackgroundProcess: () => false, diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts index 7f6980be7..01848e1ab 100644 --- a/packages/taler-wallet-webextension/src/platform/firefox.ts +++ b/packages/taler-wallet-webextension/src/platform/firefox.ts @@ -19,11 +19,10 @@ import { CrossBrowserPermissionsApi, ForegroundPlatformAPI, Permissions, + Settings, + defaultSettings, } from "./api.js"; import chromePlatform, { - containsHostPermissions as chromeHostContains, - removeHostPermissions as chromeHostRemove, - requestHostPermissions as chromeHostRequest, containsClipboardPermissions as chromeClipContains, removeClipboardPermissions as chromeClipRemove, requestClipboardPermissions as chromeClipRequest, @@ -32,6 +31,7 @@ import chromePlatform, { const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { ...chromePlatform, isFirefox, + getSettingsFromStorage, getPermissionsApi, notifyWhenAppIsReady, redirectTabToWalletPage, @@ -51,25 +51,43 @@ function addPermissionsListener(callback: (p: Permissions) => void): void { function getPermissionsApi(): CrossBrowserPermissionsApi { return { addPermissionsListener, - containsHostPermissions: chromeHostContains, - requestHostPermissions: chromeHostRequest, - removeHostPermissions: chromeHostRemove, + // containsHostPermissions: chromeHostContains, + // requestHostPermissions: chromeHostRequest, + // removeHostPermissions: chromeHostRemove, containsClipboardPermissions: chromeClipContains, removeClipboardPermissions: chromeClipRemove, requestClipboardPermissions: chromeClipRequest, }; } +async function getSettingsFromStorage(): Promise<Settings> { + //@ts-ignore + const data = await browser.storage.local.get("wallet-settings"); + if (!data) return defaultSettings; + const settings = data["wallet-settings"]; + if (!settings) return defaultSettings; + try { + const parsed = JSON.parse(settings); + return parsed; + } catch (e) { + return defaultSettings; + } +} + /** * * @param callback function to be called */ -function notifyWhenAppIsReady(callback: () => void): void { - if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) { - callback(); - } else { - window.addEventListener("load", callback); - } +function notifyWhenAppIsReady(): Promise<void> { + return new Promise((resolve) => { + if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) { + resolve(); + } else { + window.addEventListener("load", () => { + resolve(); + }); + } + }); } function redirectTabToWalletPage(tabId: number, page: string): void { |