diff options
author | Sebastian <sebasjm@gmail.com> | 2023-10-26 15:26:58 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-10-26 15:26:58 -0300 |
commit | c19aa60d3687260d01404f94e0902fe1943f16df (patch) | |
tree | 027723116667ad0a9f22a92ff472f41b0947da7c /packages/taler-wallet-webextension/src/platform | |
parent | a4c7bc4b284fe7dc4c65ceaad96fc67c40c9a708 (diff) | |
download | wallet-core-c19aa60d3687260d01404f94e0902fe1943f16df.tar.xz |
first take on adding auto open again, WIP
Diffstat (limited to 'packages/taler-wallet-webextension/src/platform')
4 files changed, 212 insertions, 92 deletions
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts index 44b5959a8..56d668a97 100644 --- a/packages/taler-wallet-webextension/src/platform/api.ts +++ b/packages/taler-wallet-webextension/src/platform/api.ts @@ -16,20 +16,18 @@ import { CoreApiResponse, - NotificationType, TalerUri, - WalletNotification, + WalletNotification } from "@gnu-taler/taler-util"; import { WalletConfig, - WalletConfigParameter, - WalletOperations, + WalletOperations } from "@gnu-taler/taler-wallet-core"; -import { BackgroundOperations } from "../wxApi.js"; import { ExtensionOperations, MessageFromExtension, } from "../taler-wallet-interaction-loader.js"; +import { BackgroundOperations } from "../wxApi.js"; export interface Permissions { /** @@ -48,9 +46,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>; @@ -102,6 +100,7 @@ type WebexWalletConfig = { export interface Settings extends WebexWalletConfig { injectTalerSupport: boolean; + autoOpenByHeader: boolean; advanceMode: boolean; backup: boolean; langSelector: boolean; @@ -112,6 +111,7 @@ export interface Settings extends WebexWalletConfig { export const defaultSettings: Settings = { injectTalerSupport: true, + autoOpenByHeader: true, advanceMode: false, backup: false, langSelector: false, @@ -206,6 +206,15 @@ export interface BackgroundPlatformAPI { message: MessageFromFrontend<Op> & { id: string }, ) => Promise<MessageResponse>, ): void; + + /** + * Backend API + */ + registerTalerHeaderListener( + onHeader: (tabId: number, url: string) => void, + ): void; + + containsTalerHeaderListener(): boolean; } export interface ForegroundPlatformAPI { /** diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts index b0934f107..3151bd6ab 100644 --- a/packages/taler-wallet-webextension/src/platform/chrome.ts +++ b/packages/taler-wallet-webextension/src/platform/chrome.ts @@ -59,6 +59,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { useServiceWorkerAsBackgroundProcess, keepAlive, listenNetworkConnectionState, + registerTalerHeaderListener, + containsTalerHeaderListener, }; export default api; @@ -95,10 +97,6 @@ function isFirefox(): boolean { return false; } -// const hostPermissions = { -// permissions: ["webRequest"], -// origins: ["http://*/*", "https://*/*"], -// }; export function containsClipboardPermissions(): Promise<boolean> { return new Promise((res, rej) => { @@ -113,18 +111,6 @@ 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 async function requestClipboardPermissions(): Promise<boolean> { return new Promise((res, rej) => { res(false); @@ -138,67 +124,7 @@ 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 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) => { @@ -225,9 +151,9 @@ function addPermissionsListener( function getPermissionsApi(): CrossBrowserPermissionsApi { return { addPermissionsListener, - // containsHostPermissions, - // requestHostPermissions, - // removeHostPermissions, + containsHostPermissions, + requestHostPermissions, + removeHostPermissions, requestClipboardPermissions, removeClipboardPermissions, containsClipboardPermissions, @@ -365,7 +291,7 @@ async function sendMessageToBackground< const timerId = setTimeout(() => { timedout = true; reject(TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {}) ); - }, 20 * 1000); //five seconds + }, 20 * 1000); chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => { if (timedout) { return false; //already rejected @@ -782,3 +708,183 @@ function listenNetworkConnectionState( window.removeEventListener("online", notifyOnline); }; } + +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; + + +function containsTalerHeaderListener(): boolean { + return ( + currentHeaderListener !== undefined || currentTabListener !== undefined + ); +} +function registerTalerHeaderListener( + callback: (tabId: number, url: string) => void, +): void { + logger.info("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 hostPermissions = { + permissions: ["webRequest"], + origins: ["http://*/*", "https://*/*"], +}; + +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 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); + }); + }); +} + +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); + }); + }); +}
\ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts index 976ac05f5..218422ded 100644 --- a/packages/taler-wallet-webextension/src/platform/dev.ts +++ b/packages/taler-wallet-webextension/src/platform/dev.ts @@ -44,6 +44,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { removeClipboardPermissions: async () => false, requestClipboardPermissions: async () => false, }), + registerTalerHeaderListener: () => false, + containsTalerHeaderListener: () => false, getWalletWebExVersion: () => ({ version: "none", }), diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts index 9f666e7ae..cc734ebf7 100644 --- a/packages/taler-wallet-webextension/src/platform/firefox.ts +++ b/packages/taler-wallet-webextension/src/platform/firefox.ts @@ -26,6 +26,9 @@ import chromePlatform, { containsClipboardPermissions as chromeClipContains, removeClipboardPermissions as chromeClipRemove, requestClipboardPermissions as chromeClipRequest, + containsHostPermissions as chromeHostContains, + requestHostPermissions as chromeHostRequest, + removeHostPermissions as chromeHostRemove, } from "./chrome.js"; const api: BackgroundPlatformAPI & ForegroundPlatformAPI = { @@ -51,9 +54,9 @@ 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, |