summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/platform')
-rw-r--r--packages/taler-wallet-webextension/src/platform/api.ts90
-rw-r--r--packages/taler-wallet-webextension/src/platform/chrome.ts397
-rw-r--r--packages/taler-wallet-webextension/src/platform/dev.ts149
-rw-r--r--packages/taler-wallet-webextension/src/platform/firefox.ts30
4 files changed, 375 insertions, 291 deletions
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts
index 4713d5d00..e66185b95 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -22,12 +22,11 @@ export interface Permissions {
*/
permissions?: string[] | undefined;
/**
- * List of origin permissions. Anything listed here must be a subset of a
- * host that appears in the optional_permissions list in the manifest.
- *
+ * List of origin permissions. Anything listed here must be a subset of a
+ * host that appears in the optional_permissions list in the manifest.
+ *
*/
origins?: string[] | undefined;
-
}
/**
@@ -38,8 +37,9 @@ export interface CrossBrowserPermissionsApi {
requestHostPermissions(): Promise<boolean>;
removeHostPermissions(): Promise<boolean>;
- addPermissionsListener(callback: (p: Permissions, lastError?: string) => void): void;
-
+ addPermissionsListener(
+ callback: (p: Permissions, lastError?: string) => void,
+ ): void;
}
export type MessageFromBackend = {
@@ -57,12 +57,12 @@ export interface WalletVersion {
*/
export interface PlatformAPI {
/**
- * Garantee that the
+ * Garantee that the
*/
keepAlive(cb: VoidFunction): void;
/**
* FIXME: should not be needed
- *
+ *
* check if the platform is firefox
*/
isFirefox(): boolean;
@@ -74,46 +74,46 @@ export interface PlatformAPI {
/**
* Backend API
- *
+ *
* Register a callback to be called when the wallet is ready to start
- * @param callback
+ * @param callback
*/
notifyWhenAppIsReady(callback: () => void): void;
/**
* Popup API
- *
+ *
* Used when an TalerURI is found and open up from the popup UI.
* Closes the popup and open the URI into the wallet UI.
- *
- * @param talerUri
+ *
+ * @param talerUri
*/
openWalletURIFromPopup(talerUri: string): void;
/**
* Backend API
- *
+ *
* Open a page into the wallet UI
- * @param page
+ * @param page
*/
openWalletPage(page: string): void;
/**
* Popup API
- *
+ *
* Open a page into the wallet UI and closed the popup
- * @param page
+ * @param page
*/
openWalletPageFromPopup(page: string): void;
/**
* Backend API
- *
+ *
* When a tab has been detected to have a Taler action the background process
* can use this function to redirect the tab to the wallet UI
- *
- * @param tabId
- * @param page
+ *
+ * @param tabId
+ * @param page
*/
redirectTabToWalletPage(tabId: number, page: string): void;
@@ -122,7 +122,6 @@ export interface PlatformAPI {
*/
getWalletVersion(): WalletVersion;
-
/**
* Backend API
*/
@@ -134,7 +133,9 @@ export interface PlatformAPI {
/**
* Backend API
*/
- registerTalerHeaderListener(onHeader: (tabId: number, url: string) => void): void;
+ registerTalerHeaderListener(
+ onHeader: (tabId: number, url: string) => void,
+ ): void;
/**
* Frontend API
*/
@@ -146,49 +147,60 @@ export interface PlatformAPI {
/**
* Backend API
- *
- * Check if background process run as service worker. This is used from the
+ *
+ * Check if background process run as service worker. This is used from the
* wallet use different http api and crypto worker.
*/
useServiceWorkerAsBackgroundProcess(): boolean;
/**
* Popup API
- *
+ *
* Read the current tab html and try to find any Taler URI or QR code present.
- *
+ *
* @return Taler URI if found
*/
findTalerUriInActiveTab(): Promise<string | undefined>;
/**
* Used from the frontend to send commands to the wallet
- *
- * @param operation
- * @param payload
- *
+ *
+ * @param operation
+ * @param payload
+ *
* @return response from the backend
*/
- sendMessageToWalletBackground(operation: string, payload: any): Promise<CoreApiResponse>;
+ sendMessageToWalletBackground(
+ operation: string,
+ payload: any,
+ ): Promise<CoreApiResponse>;
/**
* Used from the frontend to receive notifications about new information
- * @param listener
- * @return function to unsubscribe the listener
+ * @param listener
+ * @return function to unsubscribe the listener
*/
- listenToWalletBackground(listener: (message: MessageFromBackend) => void): () => void;
+ listenToWalletBackground(
+ listener: (message: MessageFromBackend) => void,
+ ): () => void;
/**
* Use by the wallet backend to receive operations from frontend (popup & wallet)
* and send a response back.
- *
- * @param onNewMessage
+ *
+ * @param onNewMessage
*/
- listenToAllChannels(onNewMessage: (message: any, sender: any, sendResponse: (r: CoreApiResponse) => void) => void): void;
+ listenToAllChannels(
+ onNewMessage: (
+ message: any,
+ sender: any,
+ sendResponse: (r: CoreApiResponse) => void,
+ ) => void,
+ ): void;
/**
* Used by the wallet backend to send notification about new information
- * @param message
+ * @param message
*/
sendMessageToAllChannels(message: MessageFromBackend): void;
}
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts
index e097baaa1..9caf42a58 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -15,8 +15,18 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { classifyTalerUri, CoreApiResponse, Logger, TalerUriType } from "@gnu-taler/taler-util";
-import { CrossBrowserPermissionsApi, MessageFromBackend, Permissions, PlatformAPI } from "./api.js";
+import {
+ classifyTalerUri,
+ CoreApiResponse,
+ Logger,
+ TalerUriType,
+} from "@gnu-taler/taler-util";
+import {
+ CrossBrowserPermissionsApi,
+ MessageFromBackend,
+ Permissions,
+ PlatformAPI,
+} from "./api.js";
const api: PlatformAPI = {
isFirefox,
@@ -39,7 +49,7 @@ const api: PlatformAPI = {
useServiceWorkerAsBackgroundProcess,
containsTalerHeaderListener,
keepAlive,
-}
+};
export default api;
@@ -47,16 +57,15 @@ const logger = new Logger("chrome.ts");
function keepAlive(callback: any): void {
if (extensionIsManifestV3()) {
- chrome.alarms.create("wallet-worker", { periodInMinutes: 1 })
+ chrome.alarms.create("wallet-worker", { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((a) => {
- logger.trace(`kee p alive alarm: ${a.name}`)
+ logger.trace(`kee p alive alarm: ${a.name}`);
// callback()
- })
+ });
// } else {
}
callback();
-
}
function isFirefox(): boolean {
@@ -66,34 +75,35 @@ function isFirefox(): boolean {
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
+ const le = chrome.runtime.lastError?.message;
if (le) {
- rej(le)
+ rej(le);
}
- res(resp)
- })
- })
+ res(resp);
+ });
+ });
}
export async function requestHostPermissions(): Promise<boolean> {
return new Promise((res, rej) => {
chrome.permissions.request(hostPermissions, (resp) => {
- const le = chrome.runtime.lastError?.message
+ const le = chrome.runtime.lastError?.message;
if (le) {
- rej(le)
+ rej(le);
}
- res(resp)
- })
- })
+ res(resp);
+ });
+ });
}
-type HeaderListenerFunc = (details: chrome.webRequest.WebResponseHeadersDetails) => void
+type HeaderListenerFunc = (
+ details: chrome.webRequest.WebResponseHeadersDetails,
+) => void;
let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
export function containsTalerHeaderListener(): boolean {
@@ -128,57 +138,69 @@ export async function removeHostPermissions(): Promise<boolean> {
}
return new Promise((res, rej) => {
chrome.permissions.remove(hostPermissions, (resp) => {
- const le = chrome.runtime.lastError?.message
+ const le = chrome.runtime.lastError?.message;
if (le) {
- rej(le)
+ rej(le);
}
- res(resp)
- })
- })
+ res(resp);
+ });
+ });
}
-function addPermissionsListener(callback: (p: Permissions, lastError?: string) => void): void {
+function addPermissionsListener(
+ callback: (p: Permissions, lastError?: string) => void,
+): void {
chrome.permissions.onAdded.addListener((perm: Permissions) => {
const lastError = chrome.runtime.lastError?.message;
- callback(perm, lastError)
- })
+ callback(perm, lastError);
+ });
}
function getPermissionsApi(): CrossBrowserPermissionsApi {
return {
- addPermissionsListener, containsHostPermissions, requestHostPermissions, removeHostPermissions
- }
+ addPermissionsListener,
+ containsHostPermissions,
+ requestHostPermissions,
+ removeHostPermissions,
+ };
}
/**
- *
+ *
* @param callback function to be called
*/
function notifyWhenAppIsReady(callback: () => void): void {
if (extensionIsManifestV3()) {
- callback()
+ callback();
} else {
window.addEventListener("load", callback);
}
}
-
function openWalletURIFromPopup(talerUri: string): void {
const uriType = classifyTalerUri(talerUri);
let url: string | undefined = undefined;
switch (uriType) {
case TalerUriType.TalerWithdraw:
- url = chrome.runtime.getURL(`static/wallet.html#/cta/withdraw?talerWithdrawUri=${talerUri}`);
+ url = chrome.runtime.getURL(
+ `static/wallet.html#/cta/withdraw?talerWithdrawUri=${talerUri}`,
+ );
break;
case TalerUriType.TalerPay:
- url = chrome.runtime.getURL(`static/wallet.html#/cta/pay?talerPayUri=${talerUri}`);
+ url = chrome.runtime.getURL(
+ `static/wallet.html#/cta/pay?talerPayUri=${talerUri}`,
+ );
break;
case TalerUriType.TalerTip:
- url = chrome.runtime.getURL(`static/wallet.html#/cta/tip?talerTipUri=${talerUri}`);
+ url = chrome.runtime.getURL(
+ `static/wallet.html#/cta/tip?talerTipUri=${talerUri}`,
+ );
break;
case TalerUriType.TalerRefund:
- url = chrome.runtime.getURL(`static/wallet.html#/cta/refund?talerRefundUri=${talerUri}`);
+ url = chrome.runtime.getURL(
+ `static/wallet.html#/cta/refund?talerRefundUri=${talerUri}`,
+ );
break;
default:
logger.warn(
@@ -187,56 +209,54 @@ function openWalletURIFromPopup(talerUri: string): void {
return;
}
- chrome.tabs.create(
- { active: true, url, },
- () => { window.close(); },
- );
+ chrome.tabs.create({ active: true, url }, () => {
+ window.close();
+ });
}
function openWalletPage(page: string): void {
- const url = chrome.runtime.getURL(`/static/wallet.html#${page}`)
- chrome.tabs.create(
- { active: true, url, },
- );
+ const url = chrome.runtime.getURL(`/static/wallet.html#${page}`);
+ chrome.tabs.create({ active: true, url });
}
function openWalletPageFromPopup(page: string): void {
- const url = chrome.runtime.getURL(`/static/wallet.html#${page}`)
- chrome.tabs.create(
- { active: true, url, },
- () => { window.close(); },
- );
+ const url = chrome.runtime.getURL(`/static/wallet.html#${page}`);
+ chrome.tabs.create({ active: true, url }, () => {
+ window.close();
+ });
}
-async function sendMessageToWalletBackground(operation: string, payload: any): Promise<any> {
+async function sendMessageToWalletBackground(
+ operation: string,
+ payload: any,
+): Promise<any> {
return new Promise<any>((resolve, reject) => {
- logger.trace("send operation to the wallet background", operation)
+ logger.trace("send operation to the wallet background", operation);
chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
if (chrome.runtime.lastError) {
- reject(chrome.runtime.lastError.message)
+ reject(chrome.runtime.lastError.message);
}
- resolve(resp)
+ resolve(resp);
// return true to keep the channel open
return true;
- })
- })
+ });
+ });
}
let notificationPort: chrome.runtime.Port | undefined;
function listenToWalletBackground(listener: (m: any) => void): () => void {
if (notificationPort === undefined) {
- notificationPort = chrome.runtime.connect({ name: "notifications" })
+ notificationPort = chrome.runtime.connect({ name: "notifications" });
}
- notificationPort.onMessage.addListener(listener)
+ notificationPort.onMessage.addListener(listener);
function removeListener(): void {
if (notificationPort !== undefined) {
- notificationPort.onMessage.removeListener(listener)
+ notificationPort.onMessage.removeListener(listener);
}
}
- return removeListener
+ return removeListener;
}
-
const allPorts: chrome.runtime.Port[] = [];
function sendMessageToAllChannels(message: MessageFromBackend): void {
@@ -262,9 +282,15 @@ function registerAllIncomingConnections(): void {
});
}
-function listenToAllChannels(cb: (message: any, sender: any, callback: (r: CoreApiResponse) => void) => void): void {
+function listenToAllChannels(
+ cb: (
+ message: any,
+ sender: any,
+ callback: (r: CoreApiResponse) => void,
+ ) => void,
+): void {
chrome.runtime.onMessage.addListener((m, s, c) => {
- cb(m, s, c)
+ cb(m, s, c);
// keep the connection open
return true;
@@ -278,13 +304,9 @@ function registerReloadOnNewVersion(): void {
logger.info("update available:", details);
chrome.runtime.reload();
});
-
}
-function redirectTabToWalletPage(
- tabId: number,
- page: string,
-): void {
+function redirectTabToWalletPage(tabId: number, page: string): void {
const url = chrome.runtime.getURL(`/static/wallet.html#${page}`);
logger.trace("redirecting tabId: ", tabId, " to: ", url);
chrome.tabs.update(tabId, { url });
@@ -300,7 +322,9 @@ function getWalletVersion(): WalletVersion {
return manifestData;
}
-function registerTalerHeaderListener(callback: (tabId: number, url: string) => void): void {
+function registerTalerHeaderListener(
+ callback: (tabId: number, url: string) => void,
+): void {
logger.trace("setting up header listener");
function headerListener(
@@ -316,44 +340,45 @@ function registerTalerHeaderListener(callback: (tabId: number, url: string) => v
details.statusCode === 200
) {
const values = (details.responseHeaders || [])
- .filter(h => h.name.toLowerCase() === 'taler')
- .map(h => h.value)
- .filter((value): value is string => !!value)
+ .filter((h) => h.name.toLowerCase() === "taler")
+ .map((h) => h.value)
+ .filter((value): value is string => !!value);
if (values.length > 0) {
- callback(details.tabId, values[0])
+ callback(details.tabId, values[0]);
}
}
return;
}
const prevHeaderListener = currentHeaderListener;
- 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 the result was positive, add the headerListener
- if (result) {
- const listener: chrome.webRequest.WebResponseHeadersEvent | undefined = chrome?.webRequest?.onHeadersReceived;
- if (listener) {
- listener.addListener(
- headerListener,
- { urls: ["<all_urls>"] },
- ["responseHeaders"],
- );
- currentHeaderListener = headerListener;
+ getPermissionsApi()
+ .containsHostPermissions()
+ .then((result) => {
+ //if there is a handler already, remove it
+ if (
+ prevHeaderListener &&
+ chrome?.webRequest?.onHeadersReceived?.hasListener(prevHeaderListener)
+ ) {
+ chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener);
}
- }
- //notify the browser about this change, this operation is expensive
- chrome?.webRequest?.handlerBehaviorChanged(() => {
- if (chrome.runtime.lastError) {
- logger.error(JSON.stringify(chrome.runtime.lastError));
+ //if the result was positive, add the headerListener
+ if (result) {
+ const listener: chrome.webRequest.WebResponseHeadersEvent | undefined =
+ chrome?.webRequest?.onHeadersReceived;
+ if (listener) {
+ listener.addListener(headerListener, { urls: ["<all_urls>"] }, [
+ "responseHeaders",
+ ]);
+ currentHeaderListener = headerListener;
+ }
}
+ //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 = {
@@ -365,8 +390,8 @@ const alertIcons = {
"64": "/static/img/taler-alert-64.png",
"128": "/static/img/taler-alert-128.png",
"256": "/static/img/taler-alert-256.png",
- "512": "/static/img/taler-alert-512.png"
-}
+ "512": "/static/img/taler-alert-512.png",
+};
const normalIcons = {
"16": "/static/img/taler-logo-16.png",
"19": "/static/img/taler-logo-19.png",
@@ -376,70 +401,99 @@ const normalIcons = {
"64": "/static/img/taler-logo-64.png",
"128": "/static/img/taler-logo-128.png",
"256": "/static/img/taler-logo-256.png",
- "512": "/static/img/taler-logo-512.png"
-}
+ "512": "/static/img/taler-logo-512.png",
+};
function setNormalIcon(): void {
if (extensionIsManifestV3()) {
- chrome.action.setIcon({ path: normalIcons })
+ chrome.action.setIcon({ path: normalIcons });
} else {
- chrome.browserAction.setIcon({ path: normalIcons })
+ chrome.browserAction.setIcon({ path: normalIcons });
}
}
function setAlertedIcon(): void {
if (extensionIsManifestV3()) {
- chrome.action.setIcon({ path: alertIcons })
+ chrome.action.setIcon({ path: alertIcons });
} else {
- chrome.browserAction.setIcon({ path: alertIcons })
+ chrome.browserAction.setIcon({ path: alertIcons });
}
}
-
-interface OffscreenCanvasRenderingContext2D extends CanvasState, CanvasTransform, CanvasCompositing, CanvasImageSmoothing, CanvasFillStrokeStyles, CanvasShadowStyles, CanvasFilters, CanvasRect, CanvasDrawPath, CanvasUserInterface, CanvasText, CanvasDrawImage, CanvasImageData, CanvasPathDrawingStyles, CanvasTextDrawingStyles, CanvasPath {
+interface OffscreenCanvasRenderingContext2D
+ extends CanvasState,
+ CanvasTransform,
+ CanvasCompositing,
+ CanvasImageSmoothing,
+ CanvasFillStrokeStyles,
+ CanvasShadowStyles,
+ CanvasFilters,
+ CanvasRect,
+ CanvasDrawPath,
+ CanvasUserInterface,
+ CanvasText,
+ CanvasDrawImage,
+ CanvasImageData,
+ CanvasPathDrawingStyles,
+ CanvasTextDrawingStyles,
+ CanvasPath {
readonly canvas: OffscreenCanvas;
}
declare const OffscreenCanvasRenderingContext2D: {
prototype: OffscreenCanvasRenderingContext2D;
- new(): OffscreenCanvasRenderingContext2D;
-}
+ new (): OffscreenCanvasRenderingContext2D;
+};
interface OffscreenCanvas extends EventTarget {
width: number;
height: number;
- getContext(contextId: "2d", contextAttributes?: CanvasRenderingContext2DSettings): OffscreenCanvasRenderingContext2D | null;
+ getContext(
+ contextId: "2d",
+ contextAttributes?: CanvasRenderingContext2DSettings,
+ ): OffscreenCanvasRenderingContext2D | null;
}
declare const OffscreenCanvas: {
prototype: OffscreenCanvas;
- new(width: number, height: number): OffscreenCanvas;
-}
+ new (width: number, height: number): OffscreenCanvas;
+};
function createCanvas(size: number): OffscreenCanvas {
if (extensionIsManifestV3()) {
- return new OffscreenCanvas(size, size)
+ return new OffscreenCanvas(size, size);
} else {
- const c = document.createElement("canvas")
+ const c = document.createElement("canvas");
c.height = size;
c.width = size;
return c;
}
}
-
async function createImage(size: number, file: string): Promise<ImageData> {
- const r = await fetch(file)
- const b = await r.blob()
- const image = await createImageBitmap(b)
+ const r = await fetch(file);
+ const b = await r.blob();
+ const image = await createImageBitmap(b);
const canvas = createCanvas(size);
- const canvasContext = canvas.getContext('2d')!;
+ const canvasContext = canvas.getContext("2d")!;
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.drawImage(image, 0, 0, canvas.width, canvas.height);
- const imageData = canvasContext.getImageData(0, 0, canvas.width, canvas.height);
+ const imageData = canvasContext.getImageData(
+ 0,
+ 0,
+ canvas.width,
+ canvas.height,
+ );
return imageData;
}
async function registerIconChangeOnTalerContent(): Promise<void> {
- const imgs = await Promise.all(Object.entries(alertIcons).map(([key, value]) => createImage(parseInt(key, 10), value)))
- const imageData = imgs.reduce((prev, cur) => ({ ...prev, [cur.width]: cur }), {} as { [size: string]: ImageData })
+ const imgs = await Promise.all(
+ Object.entries(alertIcons).map(([key, value]) =>
+ createImage(parseInt(key, 10), value),
+ ),
+ );
+ const imageData = imgs.reduce(
+ (prev, cur) => ({ ...prev, [cur.width]: cur }),
+ {} as { [size: string]: ImageData },
+ );
if (chrome.declarativeContent) {
// using declarative content does not need host permission
@@ -447,49 +501,54 @@ async function registerIconChangeOnTalerContent(): Promise<void> {
const secureTalerUrlLookup = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
- css: ["a[href^='taler://'"]
- })
+ css: ["a[href^='taler://'"],
+ }),
],
- actions: [new chrome.declarativeContent.SetIcon({ imageData })]
+ actions: [new chrome.declarativeContent.SetIcon({ imageData })],
};
const inSecureTalerUrlLookup = {
conditions: [
new chrome.declarativeContent.PageStateMatcher({
- css: ["a[href^='taler+http://'"]
- })
+ css: ["a[href^='taler+http://'"],
+ }),
],
- actions: [new chrome.declarativeContent.SetIcon({ imageData })]
+ actions: [new chrome.declarativeContent.SetIcon({ imageData })],
};
chrome.declarativeContent.onPageChanged.removeRules(undefined, function () {
- chrome.declarativeContent.onPageChanged.addRules([secureTalerUrlLookup, inSecureTalerUrlLookup]);
+ chrome.declarativeContent.onPageChanged.addRules([
+ secureTalerUrlLookup,
+ inSecureTalerUrlLookup,
+ ]);
});
return;
}
//this browser doesn't have declarativeContent
//we need host_permission and we will check the content for changing the icon
- chrome.tabs.onUpdated.addListener(async (tabId, info: chrome.tabs.TabChangeInfo) => {
- if (tabId < 0) return;
- logger.info("tab updated", tabId, info);
- if (info.status !== "complete") return;
- const uri = await findTalerUriInTab(tabId);
- if (uri) {
- setAlertedIcon()
- } else {
- setNormalIcon()
- }
-
- });
- chrome.tabs.onActivated.addListener(async ({ tabId }: chrome.tabs.TabActiveInfo) => {
- if (tabId < 0) return;
- const uri = await findTalerUriInTab(tabId);
- if (uri) {
- setAlertedIcon()
- } else {
- setNormalIcon()
- }
- })
-
+ chrome.tabs.onUpdated.addListener(
+ async (tabId, info: chrome.tabs.TabChangeInfo) => {
+ if (tabId < 0) return;
+ logger.info("tab updated", tabId, info);
+ if (info.status !== "complete") return;
+ const uri = await findTalerUriInTab(tabId);
+ if (uri) {
+ setAlertedIcon();
+ } else {
+ setNormalIcon();
+ }
+ },
+ );
+ chrome.tabs.onActivated.addListener(
+ async ({ tabId }: chrome.tabs.TabActiveInfo) => {
+ if (tabId < 0) return;
+ const uri = await findTalerUriInTab(tabId);
+ if (uri) {
+ setAlertedIcon();
+ } else {
+ setNormalIcon();
+ }
+ },
+ );
}
function registerOnInstalled(callback: () => void): void {
@@ -498,27 +557,27 @@ function registerOnInstalled(callback: () => void): void {
chrome.runtime.onInstalled.addListener(async (details) => {
logger.info(`onInstalled with reason: "${details.reason}"`);
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
- callback()
+ callback();
}
- registerIconChangeOnTalerContent()
+ registerIconChangeOnTalerContent();
});
}
function extensionIsManifestV3(): boolean {
- return chrome.runtime.getManifest().manifest_version === 3
+ return chrome.runtime.getManifest().manifest_version === 3;
}
function useServiceWorkerAsBackgroundProcess(): boolean {
- return extensionIsManifestV3()
+ return extensionIsManifestV3();
}
function searchForTalerLinks(): string | undefined {
let found;
- found = document.querySelector("a[href^='taler://'")
- if (found) return found.toString()
- found = document.querySelector("a[href^='taler+http://'")
- if (found) return found.toString()
- return undefined
+ found = document.querySelector("a[href^='taler://'");
+ if (found) return found.toString();
+ found = document.querySelector("a[href^='taler+http://'");
+ if (found) return found.toString();
+ return undefined;
}
async function getCurrentTab(): Promise<chrome.tabs.Tab> {
@@ -526,12 +585,12 @@ async function getCurrentTab(): Promise<chrome.tabs.Tab> {
return new Promise<chrome.tabs.Tab>((resolve, reject) => {
chrome.tabs.query(queryOptions, (tabs) => {
if (chrome.runtime.lastError) {
- reject(chrome.runtime.lastError)
+ reject(chrome.runtime.lastError);
return;
}
- resolve(tabs[0])
+ resolve(tabs[0]);
});
- })
+ });
}
async function findTalerUriInTab(tabId: number): Promise<string | undefined> {
@@ -541,16 +600,17 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> {
const res = await chrome.scripting.executeScript({
target: { tabId, allFrames: true },
func: searchForTalerLinks,
- args: []
- })
- return res[0].result
+ args: [],
+ });
+ return res[0].result;
} catch (e) {
return;
}
} else {
return new Promise((resolve, reject) => {
//manifest v2
- chrome.tabs.executeScript(tabId,
+ chrome.tabs.executeScript(
+ tabId,
{
code: `
(() => {
@@ -576,6 +636,5 @@ async function findTalerUriInTab(tabId: number): Promise<string | undefined> {
async function findTalerUriInActiveTab(): Promise<string | undefined> {
const tab = await getCurrentTab();
if (!tab || tab.id === undefined) return;
- return findTalerUriInTab(tab.id)
+ return findTalerUriInTab(tab.id);
}
-
diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts
index 59e6738c5..fbbb4d231 100644
--- a/packages/taler-wallet-webextension/src/platform/dev.ts
+++ b/packages/taler-wallet-webextension/src/platform/dev.ts
@@ -17,50 +17,55 @@
import { CoreApiResponse } from "@gnu-taler/taler-util";
import { MessageFromBackend, PlatformAPI } from "./api.js";
-const frames = ["popup", "wallet"]
+const frames = ["popup", "wallet"];
-const api: PlatformAPI = ({
+const api: PlatformAPI = {
isFirefox: () => false,
keepAlive: (cb: VoidFunction) => cb(),
findTalerUriInActiveTab: async () => undefined,
- containsTalerHeaderListener: () => { return true },
+ containsTalerHeaderListener: () => {
+ return true;
+ },
getPermissionsApi: () => ({
- addPermissionsListener: () => undefined, containsHostPermissions: async () => true, removeHostPermissions: async () => false, requestHostPermissions: async () => false
+ addPermissionsListener: () => undefined,
+ containsHostPermissions: async () => true,
+ removeHostPermissions: async () => false,
+ requestHostPermissions: async () => false,
}),
getWalletVersion: () => ({
- version: 'none'
+ version: "none",
}),
notifyWhenAppIsReady: (fn: () => void) => {
- let total = frames.length
+ let total = frames.length;
function waitAndNotify(): void {
- total--
+ total--;
if (total < 1) {
- console.log('done')
- fn()
+ console.log("done");
+ fn();
}
}
- frames.forEach(f => {
- const theFrame = window.frames[f as any]
- if (theFrame.location.href === 'about:blank') {
- waitAndNotify()
+ frames.forEach((f) => {
+ const theFrame = window.frames[f as any];
+ if (theFrame.location.href === "about:blank") {
+ waitAndNotify();
} else {
- theFrame.addEventListener("load", waitAndNotify)
+ theFrame.addEventListener("load", waitAndNotify);
}
- })
+ });
},
openWalletPage: (page: string) => {
- window.frames['wallet' as any].location = `/wallet.html#${page}`
+ window.frames["wallet" as any].location = `/wallet.html#${page}`;
},
openWalletPageFromPopup: (page: string) => {
- window.parent.frames['wallet' as any].location = `/wallet.html#${page}`
- window.location.href = "about:blank"
+ window.parent.frames["wallet" as any].location = `/wallet.html#${page}`;
+ window.location.href = "about:blank";
},
openWalletURIFromPopup: (page: string) => {
- alert('openWalletURIFromPopup not implemented yet')
+ alert("openWalletURIFromPopup not implemented yet");
},
redirectTabToWalletPage: (tabId: number, page: string) => {
- alert('redirectTabToWalletPage not implemented yet')
+ alert("redirectTabToWalletPage not implemented yet");
},
registerAllIncomingConnections: () => undefined,
@@ -70,91 +75,101 @@ const api: PlatformAPI = ({
useServiceWorkerAsBackgroundProcess: () => false,
- listenToAllChannels: (fn: (m: any, s: any, c: (r: CoreApiResponse) => void) => void) => {
- window.addEventListener("message", (event: MessageEvent<IframeMessageType>) => {
- if (event.data.type !== 'command') return
- const sender = event.data.header.replyMe
- fn(event.data.body, sender, (resp: CoreApiResponse) => {
- if (event.source) {
- const msg: IframeMessageResponse = {
- type: "response",
- header: { responseId: sender },
- body: resp
+ listenToAllChannels: (
+ fn: (m: any, s: any, c: (r: CoreApiResponse) => void) => void,
+ ) => {
+ window.addEventListener(
+ "message",
+ (event: MessageEvent<IframeMessageType>) => {
+ if (event.data.type !== "command") return;
+ const sender = event.data.header.replyMe;
+ fn(event.data.body, sender, (resp: CoreApiResponse) => {
+ if (event.source) {
+ const msg: IframeMessageResponse = {
+ type: "response",
+ header: { responseId: sender },
+ body: resp,
+ };
+ window.parent.postMessage(msg);
}
- window.parent.postMessage(msg)
- }
- })
- })
+ });
+ },
+ );
},
sendMessageToAllChannels: (message: MessageFromBackend) => {
- Array.from(window.frames).forEach(w => {
+ Array.from(window.frames).forEach((w) => {
try {
w.postMessage({
- header: {}, body: message
+ header: {},
+ body: message,
});
} catch (e) {
- console.error(e)
+ console.error(e);
}
- })
+ });
},
listenToWalletBackground: (onNewMessage: (m: MessageFromBackend) => void) => {
function listener(event: MessageEvent<IframeMessageType>): void {
- if (event.data.type !== 'notification') return
- onNewMessage(event.data.body)
+ if (event.data.type !== "notification") return;
+ onNewMessage(event.data.body);
}
- window.parent.addEventListener("message", listener)
+ window.parent.addEventListener("message", listener);
return () => {
- window.parent.removeEventListener("message", listener)
- }
+ window.parent.removeEventListener("message", listener);
+ };
},
sendMessageToWalletBackground: async (operation: string, payload: any) => {
- const replyMe = `reply-${Math.floor(Math.random() * 100000)}`
+ const replyMe = `reply-${Math.floor(Math.random() * 100000)}`;
const message: IframeMessageCommand = {
- type: 'command',
+ type: "command",
header: { replyMe },
- body: { operation, payload, id: "(none)" }
- }
- window.parent.postMessage(message)
+ body: { operation, payload, id: "(none)" },
+ };
+ window.parent.postMessage(message);
return new Promise((res, rej) => {
function listener(event: MessageEvent<IframeMessageType>): void {
- if (event.data.type !== "response" || event.data.header.responseId !== replyMe) {
- return
+ if (
+ event.data.type !== "response" ||
+ event.data.header.responseId !== replyMe
+ ) {
+ return;
}
- res(event.data.body)
- window.parent.removeEventListener("message", listener)
+ res(event.data.body);
+ window.parent.removeEventListener("message", listener);
}
- window.parent.addEventListener("message", listener, {
-
- })
- })
-
+ window.parent.addEventListener("message", listener, {});
+ });
},
-})
+};
-type IframeMessageType = IframeMessageNotification | IframeMessageResponse | IframeMessageCommand;
+type IframeMessageType =
+ | IframeMessageNotification
+ | IframeMessageResponse
+ | IframeMessageCommand;
interface IframeMessageNotification {
type: "notification";
- header: Record<string, never>,
- body: MessageFromBackend
+ header: Record<string, never>;
+ body: MessageFromBackend;
}
interface IframeMessageResponse {
type: "response";
header: {
responseId: string;
- },
- body: CoreApiResponse
+ };
+ body: CoreApiResponse;
}
interface IframeMessageCommand {
type: "command";
header: {
replyMe: string;
- },
+ };
body: {
- operation: any, id: string, payload: any
- }
+ operation: any;
+ id: string;
+ payload: any;
+ };
}
export default api;
-
diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts
index d5beda324..32733f6c4 100644
--- a/packages/taler-wallet-webextension/src/platform/firefox.ts
+++ b/packages/taler-wallet-webextension/src/platform/firefox.ts
@@ -15,7 +15,11 @@
*/
import { CrossBrowserPermissionsApi, Permissions, PlatformAPI } from "./api.js";
-import chromePlatform, { containsHostPermissions as chromeContains, removeHostPermissions as chromeRemove, requestHostPermissions as chromeRequest } from "./chrome.js";
+import chromePlatform, {
+ containsHostPermissions as chromeContains,
+ removeHostPermissions as chromeRemove,
+ requestHostPermissions as chromeRequest,
+} from "./chrome.js";
const api: PlatformAPI = {
...chromePlatform,
@@ -23,18 +27,17 @@ const api: PlatformAPI = {
getPermissionsApi,
notifyWhenAppIsReady,
redirectTabToWalletPage,
- useServiceWorkerAsBackgroundProcess
+ useServiceWorkerAsBackgroundProcess,
};
export default api;
function isFirefox(): boolean {
- return true
+ return true;
}
-
function addPermissionsListener(callback: (p: Permissions) => void): void {
- console.log("addPermissionListener is not supported for Firefox")
+ console.log("addPermissionListener is not supported for Firefox");
}
function getPermissionsApi(): CrossBrowserPermissionsApi {
@@ -42,33 +45,28 @@ function getPermissionsApi(): CrossBrowserPermissionsApi {
addPermissionsListener,
containsHostPermissions: chromeContains,
requestHostPermissions: chromeRequest,
- removeHostPermissions: chromeRemove
- }
+ removeHostPermissions: chromeRemove,
+ };
}
/**
- *
+ *
* @param callback function to be called
*/
function notifyWhenAppIsReady(callback: () => void): void {
if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) {
- callback()
+ callback();
} else {
window.addEventListener("load", callback);
}
}
-
-function redirectTabToWalletPage(
- tabId: number,
- page: string,
-): void {
+function redirectTabToWalletPage(tabId: number, page: string): void {
const url = chrome.runtime.getURL(`/static/wallet.html#${page}`);
console.log("redirecting tabId: ", tabId, " to: ", url);
chrome.tabs.update(tabId, { url, loadReplace: true } as any);
}
-
function useServiceWorkerAsBackgroundProcess(): false {
- return false
+ return false;
}