diff --git a/packages/taler-wallet-webextension/src/api/browser.ts b/packages/taler-wallet-webextension/src/api/browser.ts
deleted file mode 100644
index b69a49680..000000000
--- a/packages/taler-wallet-webextension/src/api/browser.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-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
-}
-
-async function getCurrentTab() {
- let queryOptions = { active: true, currentWindow: true };
- let [tab] = await chrome.tabs.query(queryOptions);
- return tab;
-}
-
-
-
-export async function findTalerUriInActiveTab(): Promise {
- if (chrome.runtime.getManifest().manifest_version === 3) {
- // manifest v3
- const tab = await getCurrentTab();
- const res = await chrome.scripting.executeScript({
- target: {
- tabId: tab.id!,
- allFrames: true,
- } as any,
- func: searchForTalerLinks,
- args: []
- })
- return res[0].result
- }
- return new Promise((resolve, reject) => {
- //manifest v2
- chrome.tabs.executeScript(
- {
- code: `
- (() => {
- let x = document.querySelector("a[href^='taler://'") || document.querySelector("a[href^='taler+http://'");
- return x ? x.href.toString() : null;
- })();
- `,
- allFrames: false,
- },
- (result) => {
- if (chrome.runtime.lastError) {
- console.error(chrome.runtime.lastError);
- resolve(undefined);
- return;
- }
- resolve(result[0]);
- },
- );
- });
-}
diff --git a/packages/taler-wallet-webextension/src/background.ts b/packages/taler-wallet-webextension/src/background.ts
index d6aeddc1d..9c572c176 100644
--- a/packages/taler-wallet-webextension/src/background.ts
+++ b/packages/taler-wallet-webextension/src/background.ts
@@ -23,14 +23,31 @@
/**
* Imports.
*/
+import { platform, setupPlatform } from "./platform/api";
+import firefoxAPI from "./platform/firefox"
+import chromeAPI from "./platform/chrome"
import { wxMain } from "./wxBackend";
-const loadedFromWebpage = typeof window !== "undefined"
+const isFirefox = typeof (window as any)['InstallTrigger'] !== 'undefined'
-if (chrome.runtime.getManifest().manifest_version === 3) {
- wxMain();
+//FIXME: create different entry point for any platform instead of
+//switching in runtime
+if (isFirefox) {
+ console.log("Wallet setup for Firefox API")
+ setupPlatform(firefoxAPI)
} else {
- window.addEventListener("load", () => {
- wxMain();
- });
+ console.log("Wallet setup for Chrome API")
+ setupPlatform(chromeAPI)
}
+
+try {
+ platform.registerOnInstalled(() => {
+ platform.openWalletPage("/welcome")
+ })
+} catch (e) {
+ console.error(e);
+}
+
+platform.notifyWhenAppIsReady(() => {
+ wxMain();
+})
diff --git a/packages/taler-wallet-webextension/src/chromeBadge.ts b/packages/taler-wallet-webextension/src/chromeBadge.ts
index 60585793e..74c2fcd2f 100644
--- a/packages/taler-wallet-webextension/src/chromeBadge.ts
+++ b/packages/taler-wallet-webextension/src/chromeBadge.ts
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see
*/
-import { isFirefox } from "./compat";
+import { platform } from "./platform/api";
/**
* Polyfill for requestAnimationFrame, which
@@ -210,7 +210,7 @@ export class ChromeBadge {
if (this.animationRunning) {
return;
}
- if (isFirefox()) {
+ if (platform.isFirefox()) {
// Firefox does not support badge animations properly
return;
}
diff --git a/packages/taler-wallet-webextension/src/compat.ts b/packages/taler-wallet-webextension/src/compat.ts
deleted file mode 100644
index b17d0fb80..000000000
--- a/packages/taler-wallet-webextension/src/compat.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 INRIA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see
- */
-
-/**
- * Compatibility helpers needed for browsers that don't implement
- * WebExtension APIs consistently.
- */
-
-// globalThis polyfill, see https://mathiasbynens.be/notes/globalthis
-(function () {
- if (typeof globalThis === "object") return;
- Object.defineProperty(Object.prototype, "__magic__", {
- get: function () {
- return this;
- },
- configurable: true, // This makes it possible to `delete` the getter later.
- });
- // @ts-ignore: polyfill magic
- __magic__.globalThis = __magic__; // lolwat
- // @ts-ignore: polyfill magic
- delete Object.prototype.__magic__;
-})();
-
-export function isFirefox(): boolean {
- const rt = chrome.runtime as any;
- if (typeof rt.getBrowserInfo === "function") {
- return true;
- }
- return false;
-}
-
-/**
- * Check if we are running under nodejs.
- */
-export function isNode(): boolean {
- return typeof process !== "undefined" && process.release.name === "node";
-}
-
-/**
- * Compatibility API that works on multiple browsers.
- */
-export interface CrossBrowserPermissionsApi {
- contains(
- permissions: chrome.permissions.Permissions,
- callback: (result: boolean) => void,
- ): void;
-
- addPermissionsListener(
- callback: (permissions: chrome.permissions.Permissions) => void,
- ): void;
-
- request(
- permissions: chrome.permissions.Permissions,
- callback?: (granted: boolean) => void,
- ): void;
-
- remove(
- permissions: chrome.permissions.Permissions,
- callback?: (removed: boolean) => void,
- ): void;
-}
-
-export function getPermissionsApi(): CrossBrowserPermissionsApi {
- const myBrowser = (globalThis as any).browser;
- if (
- typeof myBrowser === "object" &&
- typeof myBrowser.permissions === "object"
- ) {
- return {
- addPermissionsListener: () => {
- console.log("not supported for firefox")
- // Not supported yet.
- },
- contains: myBrowser.permissions.contains,
- request: myBrowser.permissions.request,
- remove: myBrowser.permissions.remove,
- };
- } else {
- return {
- addPermissionsListener: chrome.permissions.onAdded.addListener.bind(
- chrome.permissions.onAdded,
- ),
- contains: chrome.permissions.contains,
- request: chrome.permissions.request,
- remove: chrome.permissions.remove,
- };
- }
-}
diff --git a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
index 0998cab7b..0cffff693 100644
--- a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
+++ b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
@@ -17,7 +17,6 @@
import { WalletDiagnostics } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact";
import { useTranslationContext } from "../context/translation";
-import { PageLink } from "../renderHtml";
interface Props {
timedOut: boolean;
@@ -70,10 +69,7 @@ export function Diagnostics({ timedOut, diagnostics }: Props): VNode {
Your wallet database is outdated. Currently automatic migration is
- not supported. Please go{" "}
-
- here
- {" "}
+ not supported. Please go here
to reset the wallet database.
diff --git a/packages/taler-wallet-webextension/src/context/iocContext.ts b/packages/taler-wallet-webextension/src/context/iocContext.ts
index 688e7b488..a24b0c1ca 100644
--- a/packages/taler-wallet-webextension/src/context/iocContext.ts
+++ b/packages/taler-wallet-webextension/src/context/iocContext.ts
@@ -21,7 +21,7 @@
import { createContext, h, VNode } from "preact";
import { useContext } from "preact/hooks";
-import { findTalerUriInActiveTab } from "../api/browser";
+import { platform } from "../platform/api";
interface Type {
findTalerUriInActiveTab: () => Promise;
@@ -45,5 +45,5 @@ export const IoCProviderForTesting = ({ value, children }: { value: Type, childr
};
export const IoCProviderForRuntime = ({ children }: { children: any }): VNode => {
- return h(Context.Provider, { value: { findTalerUriInActiveTab }, children });
+ return h(Context.Provider, { value: { findTalerUriInActiveTab: platform.findTalerUriInActiveTab }, children });
};
diff --git a/packages/taler-wallet-webextension/src/cta/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx
index efc436bc8..790e8d9fa 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx
@@ -20,11 +20,15 @@
* @author sebasjm
*/
-import { Amounts, ApplyRefundResponse } from "@gnu-taler/taler-util";
+import {
+ amountFractionalBase,
+ AmountJson,
+ Amounts,
+ ApplyRefundResponse,
+} from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { useTranslationContext } from "../context/translation";
-import { AmountView } from "../renderHtml";
import * as wxApi from "../wxApi";
interface Props {
@@ -120,3 +124,27 @@ export function RefundPage({ talerRefundUri }: Props): VNode {
return ;
}
+
+export function renderAmount(amount: AmountJson | string): VNode {
+ let a;
+ if (typeof amount === "string") {
+ a = Amounts.parse(amount);
+ } else {
+ a = amount;
+ }
+ if (!a) {
+ return (invalid amount);
+ }
+ const x = a.value + a.fraction / amountFractionalBase;
+ return (
+
+ {x} {a.currency}
+
+ );
+}
+
+export const AmountView = ({
+ amount,
+}: {
+ amount: AmountJson | string;
+}): VNode => renderAmount(amount);
diff --git a/packages/taler-wallet-webextension/src/cta/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx
index 71aa04a2b..5767b5008 100644
--- a/packages/taler-wallet-webextension/src/cta/Tip.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Tip.tsx
@@ -17,15 +17,19 @@
/**
* Page shown to the user to accept or ignore a tip from a merchant.
*
- * @author sebasjm
+ * @author sebasjm
*/
-import { PrepareTipResult } from "@gnu-taler/taler-util";
+import {
+ amountFractionalBase,
+ AmountJson,
+ Amounts,
+ PrepareTipResult,
+} from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks";
import { Loading } from "../components/Loading";
import { useTranslationContext } from "../context/translation";
-import { AmountView } from "../renderHtml";
import * as wxApi from "../wxApi";
interface Props {
@@ -136,3 +140,24 @@ export function TipPage({ talerTipUri }: Props): VNode {
/>
);
}
+
+function renderAmount(amount: AmountJson | string): VNode {
+ let a;
+ if (typeof amount === "string") {
+ a = Amounts.parse(amount);
+ } else {
+ a = amount;
+ }
+ if (!a) {
+ return (invalid amount);
+ }
+ const x = a.value + a.fraction / amountFractionalBase;
+ return (
+
+ {x} {a.currency}
+
+ );
+}
+
+const AmountView = ({ amount }: { amount: AmountJson | string }): VNode =>
+ renderAmount(amount);
diff --git a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts
index 6bf6a7bdf..66d710705 100644
--- a/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useExtendedPermissions.ts
@@ -16,7 +16,7 @@
import { useState, useEffect } from "preact/hooks";
import * as wxApi from "../wxApi";
-import { getPermissionsApi } from "../compat";
+import { platform } from "../platform/api";
import { getReadRequestPermissions } from "../permissions";
export function useExtendedPermissions(): [boolean, () => Promise] {
@@ -40,24 +40,41 @@ async function handleExtendedPerm(isEnabled: boolean, onChange: (value: boolean)
if (!isEnabled) {
// We set permissions here, since apparently FF wants this to be done
// as the result of an input event ...
- return new Promise((res) => {
- getPermissionsApi().request(getReadRequestPermissions(), async (granted: boolean) => {
- console.log("permissions granted:", granted);
- if (chrome.runtime.lastError) {
- console.error("error requesting permissions");
- console.error(chrome.runtime.lastError);
- onChange(false);
- return;
- }
- try {
- const res = await wxApi.setExtendedPermissions(granted);
- onChange(res.newValue);
- } finally {
- res()
- }
+ const granted = await platform.getPermissionsApi().request(getReadRequestPermissions());
+ console.log("permissions granted:", granted);
+ const lastError = platform.getLastError();
+ if (lastError) {
+ console.error("error requesting permissions");
+ console.error(lastError);
+ onChange(false);
+ return;
+ }
+ // try {
+ const res = await wxApi.setExtendedPermissions(granted);
+ onChange(res.newValue);
+ // } finally {
+ // return
+ // }
- });
- })
+ // return new Promise((res) => {
+ // platform.getPermissionsApi().request(getReadRequestPermissions(), async (granted: boolean) => {
+ // console.log("permissions granted:", granted);
+ // const lastError = getLastError()
+ // if (lastError) {
+ // console.error("error requesting permissions");
+ // console.error(lastError);
+ // onChange(false);
+ // return;
+ // }
+ // try {
+ // const res = await wxApi.setExtendedPermissions(granted);
+ // onChange(res.newValue);
+ // } finally {
+ // res()
+ // }
+
+ // });
+ // })
}
await wxApi.setExtendedPermissions(false).then(r => onChange(r.newValue));
return
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts
new file mode 100644
index 000000000..9b4e02ffb
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -0,0 +1,90 @@
+/*
+ This file is part of TALER
+ (C) 2017 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ */
+
+import { CoreApiResponse, NotificationType, TalerUriType } from "@gnu-taler/taler-util";
+
+export interface Permissions {
+ /**
+ * List of named 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.
+ *
+ */
+ origins?: string[] | undefined;
+
+}
+
+/**
+ * Compatibility API that works on multiple browsers.
+ */
+export interface CrossBrowserPermissionsApi {
+ contains(p: Permissions): Promise;
+ request(p: Permissions): Promise;
+ remove(p: Permissions): Promise;
+
+ addPermissionsListener(callback: (p: Permissions) => void): void;
+
+}
+
+export type MessageFromBackend = {
+ type: NotificationType;
+};
+
+export interface WalletVersion {
+ version_name?: string | undefined;
+ version: string;
+}
+
+/**
+ * Compatibility helpers needed for browsers that don't implement
+ * WebExtension APIs consistently.
+ */
+export interface PlatformAPI {
+ /**
+ * check if the platform is firefox
+ */
+ isFirefox(): boolean;
+ /**
+ *
+ */
+ getPermissionsApi(): CrossBrowserPermissionsApi;
+ notifyWhenAppIsReady(callback: () => void): void;
+ openWalletURIFromPopup(uriType: TalerUriType, talerUri: string): void;
+ openWalletPage(page: string): void;
+ openWalletPageFromPopup(page: string): void;
+ setMessageToWalletBackground(operation: string, payload: any): Promise;
+ listenToWalletNotifications(listener: (m: any) => void): () => void;
+ sendMessageToAllChannels(message: MessageFromBackend): void;
+ registerAllIncomingConnections(): void;
+ registerOnNewMessage(onNewMessage: (message: any, sender: any, callback: any) => void): void;
+ registerReloadOnNewVersion(): void;
+ redirectTabToWalletPage(tabId: number, page: string): void;
+ getWalletVersion(): WalletVersion;
+ registerTalerHeaderListener(onHeader: (tabId: number, url: string) => void): void;
+ registerOnInstalled(callback: () => void): void;
+ useServiceWorkerAsBackgroundProcess(): boolean;
+ getLastError(): string | undefined;
+ searchForTalerLinks(): string | undefined;
+ findTalerUriInActiveTab(): Promise;
+}
+
+export let platform: PlatformAPI = undefined as any;
+export function setupPlatform(impl: PlatformAPI) {
+ platform = impl;
+}
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts
new file mode 100644
index 000000000..dada23c57
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -0,0 +1,371 @@
+/*
+ This file is part of TALER
+ (C) 2017 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ */
+
+import { TalerUriType } from "@gnu-taler/taler-util";
+import { getReadRequestPermissions } from "../permissions";
+import { CrossBrowserPermissionsApi, MessageFromBackend, Permissions, PlatformAPI } from "./api.js";
+
+const api: PlatformAPI = {
+ isFirefox,
+ findTalerUriInActiveTab,
+ getLastError,
+ getPermissionsApi,
+ getWalletVersion,
+ listenToWalletNotifications,
+ notifyWhenAppIsReady,
+ openWalletPage,
+ openWalletPageFromPopup,
+ openWalletURIFromPopup,
+ redirectTabToWalletPage,
+ registerAllIncomingConnections,
+ registerOnInstalled,
+ registerOnNewMessage,
+ registerReloadOnNewVersion,
+ registerTalerHeaderListener,
+ searchForTalerLinks,
+ sendMessageToAllChannels,
+ setMessageToWalletBackground,
+ useServiceWorkerAsBackgroundProcess
+}
+
+export default api;
+
+function isFirefox(): boolean {
+ return false;
+}
+
+export function contains(p: Permissions): Promise {
+ return new Promise((res, rej) => {
+ chrome.permissions.contains(p, (resp) => {
+ const le = getLastError()
+ if (le) {
+ rej(le)
+ }
+ res(resp)
+ })
+ })
+}
+
+export async function request(p: Permissions): Promise {
+ return new Promise((res, rej) => {
+ chrome.permissions.request(p, (resp) => {
+ const le = getLastError()
+ if (le) {
+ rej(le)
+ }
+ res(resp)
+ })
+ })
+}
+
+export async function remove(p: Permissions): Promise {
+ return new Promise((res, rej) => {
+ chrome.permissions.remove(p, (resp) => {
+ const le = getLastError()
+ if (le) {
+ rej(le)
+ }
+ res(resp)
+ })
+ })
+}
+
+function addPermissionsListener(callback: (p: Permissions) => void): void {
+ console.log("addPermissionListener is not supported for Firefox");
+ chrome.permissions.onAdded.addListener(callback)
+}
+
+function getPermissionsApi(): CrossBrowserPermissionsApi {
+ return {
+ addPermissionsListener, contains, request, remove
+ }
+}
+
+/**
+ *
+ * @param callback function to be called
+ */
+function notifyWhenAppIsReady(callback: () => void) {
+ if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) {
+ callback()
+ } else {
+ window.addEventListener("load", callback);
+ }
+}
+
+
+function openWalletURIFromPopup(uriType: TalerUriType, talerUri: string) {
+ let url: string | undefined = undefined;
+ switch (uriType) {
+ case TalerUriType.TalerWithdraw:
+ 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}`);
+ break;
+ case TalerUriType.TalerTip:
+ 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}`);
+ break;
+ default:
+ console.warn(
+ "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
+ );
+ return;
+ }
+
+ chrome.tabs.create(
+ { active: true, url, },
+ () => { window.close(); },
+ );
+}
+
+function openWalletPage(page: string) {
+ const url = chrome.runtime.getURL(`/static/wallet.html#${page}`)
+ chrome.tabs.create(
+ { active: true, url, },
+ );
+}
+
+function openWalletPageFromPopup(page: string) {
+ const url = chrome.runtime.getURL(`/static/wallet.html#${page}`)
+ chrome.tabs.create(
+ { active: true, url, },
+ () => { window.close(); },
+ );
+}
+
+async function setMessageToWalletBackground(operation: string, payload: any): Promise {
+ return new Promise((resolve, reject) => {
+ chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
+ if (chrome.runtime.lastError) {
+ reject(chrome.runtime.lastError.message)
+ }
+ resolve(resp)
+ // return true to keep the channel open
+ return true;
+ })
+ })
+}
+
+let notificationPort: chrome.runtime.Port | undefined;
+function listenToWalletNotifications(listener: (m: any) => void) {
+ if (notificationPort === undefined) {
+ notificationPort = chrome.runtime.connect({ name: "notifications" })
+ }
+ notificationPort.onMessage.addListener(listener)
+ function removeListener() {
+ if (notificationPort !== undefined) {
+ notificationPort.onMessage.removeListener(listener)
+ }
+ }
+ return removeListener
+}
+
+
+const allPorts: chrome.runtime.Port[] = [];
+
+function sendMessageToAllChannels(message: MessageFromBackend) {
+ for (const notif of allPorts) {
+ // const message: MessageFromBackend = { type: msg.type };
+ try {
+ notif.postMessage(message);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+}
+
+function registerAllIncomingConnections() {
+ chrome.runtime.onConnect.addListener((port) => {
+ allPorts.push(port);
+ port.onDisconnect.addListener((discoPort) => {
+ const idx = allPorts.indexOf(discoPort);
+ if (idx >= 0) {
+ allPorts.splice(idx, 1);
+ }
+ });
+ });
+}
+
+function registerOnNewMessage(cb: (message: any, sender: any, callback: any) => void) {
+ chrome.runtime.onMessage.addListener((m, s, c) => {
+ cb(m, s, c)
+
+ // keep the connection open
+ return true;
+ });
+}
+
+function registerReloadOnNewVersion() {
+ // Explicitly unload the extension page as soon as an update is available,
+ // so the update gets installed as soon as possible.
+ chrome.runtime.onUpdateAvailable.addListener((details) => {
+ console.log("update available:", details);
+ chrome.runtime.reload();
+ });
+
+}
+
+function redirectTabToWalletPage(
+ tabId: number,
+ page: string,
+) {
+ const url = chrome.runtime.getURL(`/static/wallet.html#${page}`);
+ console.log("redirecting tabId: ", tabId, " to: ", url);
+ chrome.tabs.update(tabId, { url });
+}
+
+interface WalletVersion {
+ version_name?: string | undefined;
+ version: string;
+}
+
+function getWalletVersion(): WalletVersion {
+ const manifestData = chrome.runtime.getManifest();
+ return manifestData;
+}
+
+
+function registerTalerHeaderListener(callback: (tabId: number, url: string) => void): void {
+ console.log("setting up header listener");
+
+ function headerListener(
+ details: chrome.webRequest.WebResponseHeadersDetails,
+ ) {
+ if (chrome.runtime.lastError) {
+ console.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) {
+ callback(details.tabId, values[0])
+ }
+ }
+ return;
+ }
+
+ getPermissionsApi().contains(getReadRequestPermissions()).then(result => {
+ //if there is a handler already, remove it
+ if (
+ "webRequest" in chrome &&
+ "onHeadersReceived" in chrome.webRequest &&
+ chrome.webRequest.onHeadersReceived.hasListener(headerListener)
+ ) {
+ chrome.webRequest.onHeadersReceived.removeListener(headerListener);
+ }
+ //if the result was positive, add the headerListener
+ if (result) {
+ chrome.webRequest.onHeadersReceived.addListener(
+ headerListener,
+ { urls: [""] },
+ ["responseHeaders"],
+ );
+ }
+ //notify the browser about this change, this operation is expensive
+ if ("webRequest" in chrome) {
+ chrome.webRequest.handlerBehaviorChanged(() => {
+ if (chrome.runtime.lastError) {
+ console.error(JSON.stringify(chrome.runtime.lastError));
+ }
+ });
+ }
+ });
+}
+
+function registerOnInstalled(callback: () => void) {
+ // This needs to be outside of main, as Firefox won't fire the event if
+ // the listener isn't created synchronously on loading the backend.
+ chrome.runtime.onInstalled.addListener((details) => {
+ console.log("onInstalled with reason", details.reason);
+ if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
+ callback()
+ }
+ });
+}
+
+function useServiceWorkerAsBackgroundProcess() {
+ return chrome.runtime.getManifest().manifest_version === 3
+}
+
+function getLastError() {
+ return chrome.runtime.lastError?.message;
+}
+
+
+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
+}
+
+async function getCurrentTab() {
+ let queryOptions = { active: true, currentWindow: true };
+ let [tab] = await chrome.tabs.query(queryOptions);
+ return tab;
+}
+
+
+async function findTalerUriInActiveTab(): Promise {
+ if (chrome.runtime.getManifest().manifest_version === 3) {
+ // manifest v3
+ const tab = await getCurrentTab();
+ const res = await chrome.scripting.executeScript({
+ target: {
+ tabId: tab.id!,
+ allFrames: true,
+ } as any,
+ func: searchForTalerLinks,
+ args: []
+ })
+ return res[0].result
+ }
+ return new Promise((resolve, reject) => {
+ //manifest v2
+ chrome.tabs.executeScript(
+ {
+ code: `
+ (() => {
+ let x = document.querySelector("a[href^='taler://'") || document.querySelector("a[href^='taler+http://'");
+ return x ? x.href.toString() : null;
+ })();
+ `,
+ allFrames: false,
+ },
+ (result) => {
+ if (chrome.runtime.lastError) {
+ console.error(JSON.stringify(chrome.runtime.lastError));
+ resolve(undefined);
+ return;
+ }
+ resolve(result[0]);
+ },
+ );
+ });
+}
diff --git a/packages/taler-wallet-webextension/src/platform/firefox.ts b/packages/taler-wallet-webextension/src/platform/firefox.ts
new file mode 100644
index 000000000..dad90626b
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/platform/firefox.ts
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ (C) 2017 INRIA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ */
+
+import { CrossBrowserPermissionsApi, Permissions, PlatformAPI } from "./api.js";
+import chromePlatform, { contains as chromeContains, remove as chromeRemove, request as chromeRequest } from "./chrome";
+
+const api: PlatformAPI = {
+ ...chromePlatform,
+ isFirefox,
+ getPermissionsApi,
+ notifyWhenAppIsReady,
+ redirectTabToWalletPage,
+ useServiceWorkerAsBackgroundProcess
+};
+
+export default api;
+
+function isFirefox(): boolean {
+ return true
+}
+
+
+function addPermissionsListener(callback: (p: Permissions) => void): void {
+ console.log("addPermissionListener is not supported for Firefox")
+}
+
+function getPermissionsApi(): CrossBrowserPermissionsApi {
+ return {
+ addPermissionsListener,
+ contains: chromeContains,
+ request: chromeRequest,
+ remove: chromeRemove
+ }
+}
+
+/**
+ *
+ * @param callback function to be called
+ */
+function notifyWhenAppIsReady(callback: () => void) {
+ if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) {
+ callback()
+ } else {
+ window.addEventListener("load", callback);
+ }
+}
+
+
+function redirectTabToWalletPage(
+ tabId: number,
+ page: string,
+) {
+ 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() {
+ return false
+}
diff --git a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
index 3deea032d..d47b8ce7b 100644
--- a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
@@ -375,16 +375,6 @@ function toBase64(str: string): string {
);
}
-export function reload(): void {
- try {
- // eslint-disable-next-line no-undef
- chrome.runtime.reload();
- window.close();
- } catch (e) {
- // Functionality missing in firefox, ignore!
- }
-}
-
function runIntegrationTest() {}
export async function confirmReset(
@@ -395,13 +385,3 @@ export async function confirmReset(
window.close();
}
}
-
-export function openExtensionPage(page: string) {
- return () => {
- // eslint-disable-next-line no-undef
- chrome.tabs.create({
- // eslint-disable-next-line no-undef
- url: chrome.runtime.getURL(page),
- });
- };
-}
diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
index 9ac83a578..a1082ad92 100644
--- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
+++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx
@@ -21,36 +21,21 @@
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
import { Fragment, h } from "preact";
+import { platform } from "../platform/api";
import { ButtonPrimary, ButtonSuccess } from "../components/styled";
import { useTranslationContext } from "../context/translation";
-import { actionForTalerUri } from "../utils/index";
export interface Props {
url: string;
onDismiss: () => void;
}
-async function getCurrentTab(): Promise {
- let queryOptions = { active: true, currentWindow: true };
- const tab = await new Promise((res, rej) => {
- chrome.tabs.query(queryOptions, (tabs) => {
- res(tabs[0]);
- });
- });
- return tab;
-}
-
-async function navigateTo(url?: string) {
- if (!url) return;
- const tab = await getCurrentTab();
- if (!tab.id) return;
- await chrome.tabs.update(tab.id, { url });
- window.close();
-}
-
export function TalerActionFound({ url, onDismiss }: Props) {
const uriType = classifyTalerUri(url);
const { i18n } = useTranslationContext();
+ function redirectToWallet() {
+ platform.openWalletURIFromPopup(uriType, url);
+ }
return (
@@ -62,11 +47,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
This page has pay action.
- {
- navigateTo(actionForTalerUri(uriType, url));
- }}
- >
+ Open pay page
@@ -78,11 +59,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
This page has a withdrawal action.