all the browser related code move into one place, making it easy for specific platform code or mocking for testing
This commit is contained in:
parent
c539d1803c
commit
32f6409ac3
@ -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<string | undefined> {
|
||||
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]);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
@ -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();
|
||||
})
|
||||
|
@ -14,7 +14,7 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
}
|
||||
}
|
@ -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 {
|
||||
<p>
|
||||
<i18n.Translate>
|
||||
Your wallet database is outdated. Currently automatic migration is
|
||||
not supported. Please go{" "}
|
||||
<PageLink pageName="/reset-required">
|
||||
<i18n.Translate>here</i18n.Translate>
|
||||
</PageLink>{" "}
|
||||
not supported. Please go <i18n.Translate>here</i18n.Translate>
|
||||
to reset the wallet database.
|
||||
</i18n.Translate>
|
||||
</p>
|
||||
|
@ -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<string | undefined>;
|
||||
@ -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 });
|
||||
};
|
||||
|
@ -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 <View applyResult={applyResult} />;
|
||||
}
|
||||
|
||||
export function renderAmount(amount: AmountJson | string): VNode {
|
||||
let a;
|
||||
if (typeof amount === "string") {
|
||||
a = Amounts.parse(amount);
|
||||
} else {
|
||||
a = amount;
|
||||
}
|
||||
if (!a) {
|
||||
return <span>(invalid amount)</span>;
|
||||
}
|
||||
const x = a.value + a.fraction / amountFractionalBase;
|
||||
return (
|
||||
<span>
|
||||
{x} {a.currency}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export const AmountView = ({
|
||||
amount,
|
||||
}: {
|
||||
amount: AmountJson | string;
|
||||
}): VNode => renderAmount(amount);
|
||||
|
@ -17,15 +17,19 @@
|
||||
/**
|
||||
* Page shown to the user to accept or ignore a tip from a merchant.
|
||||
*
|
||||
* @author sebasjm <dold@taler.net>
|
||||
* @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 <span>(invalid amount)</span>;
|
||||
}
|
||||
const x = a.value + a.fraction / amountFractionalBase;
|
||||
return (
|
||||
<span>
|
||||
{x} {a.currency}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const AmountView = ({ amount }: { amount: AmountJson | string }): VNode =>
|
||||
renderAmount(amount);
|
||||
|
@ -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<void>] {
|
||||
@ -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<void>((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<void>((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
|
||||
|
90
packages/taler-wallet-webextension/src/platform/api.ts
Normal file
90
packages/taler-wallet-webextension/src/platform/api.ts
Normal file
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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<boolean>;
|
||||
request(p: Permissions): Promise<boolean>;
|
||||
remove(p: Permissions): Promise<boolean>;
|
||||
|
||||
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<CoreApiResponse>;
|
||||
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<string | undefined>;
|
||||
}
|
||||
|
||||
export let platform: PlatformAPI = undefined as any;
|
||||
export function setupPlatform(impl: PlatformAPI) {
|
||||
platform = impl;
|
||||
}
|
371
packages/taler-wallet-webextension/src/platform/chrome.ts
Normal file
371
packages/taler-wallet-webextension/src/platform/chrome.ts
Normal file
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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<any> {
|
||||
return new Promise<any>((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: ["<all_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<string | undefined> {
|
||||
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]);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
74
packages/taler-wallet-webextension/src/platform/firefox.ts
Normal file
74
packages/taler-wallet-webextension/src/platform/firefox.ts
Normal file
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
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
|
||||
}
|
@ -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),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -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<chrome.tabs.Tab> {
|
||||
let queryOptions = { active: true, currentWindow: true };
|
||||
const tab = await new Promise<chrome.tabs.Tab>((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 (
|
||||
<Fragment>
|
||||
<section>
|
||||
@ -62,11 +47,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
||||
<p>
|
||||
<i18n.Translate>This page has pay action.</i18n.Translate>
|
||||
</p>
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
navigateTo(actionForTalerUri(uriType, url));
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
<i18n.Translate>Open pay page</i18n.Translate>
|
||||
</ButtonSuccess>
|
||||
</div>
|
||||
@ -78,11 +59,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
||||
This page has a withdrawal action.
|
||||
</i18n.Translate>
|
||||
</p>
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
navigateTo(actionForTalerUri(uriType, url));
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
<i18n.Translate>Open withdraw page</i18n.Translate>
|
||||
</ButtonSuccess>
|
||||
</div>
|
||||
@ -92,11 +69,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
||||
<p>
|
||||
<i18n.Translate>This page has a tip action.</i18n.Translate>
|
||||
</p>
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
navigateTo(actionForTalerUri(uriType, url));
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
<i18n.Translate>Open tip page</i18n.Translate>
|
||||
</ButtonSuccess>
|
||||
</div>
|
||||
@ -108,11 +81,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
||||
This page has a notify reserve action.
|
||||
</i18n.Translate>
|
||||
</p>
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
navigateTo(actionForTalerUri(uriType, url));
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
<i18n.Translate>Notify</i18n.Translate>
|
||||
</ButtonSuccess>
|
||||
</div>
|
||||
@ -122,11 +91,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
|
||||
<p>
|
||||
<i18n.Translate>This page has a refund action.</i18n.Translate>
|
||||
</p>
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
navigateTo(actionForTalerUri(uriType, url));
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
<i18n.Translate>Open refund page</i18n.Translate>
|
||||
</ButtonSuccess>
|
||||
</div>
|
||||
|
@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Main entry point for extension pages.
|
||||
*
|
||||
* @author sebasjm <dold@taler.net>
|
||||
* @author sebasjm
|
||||
*/
|
||||
|
||||
import { setupI18n } from "@gnu-taler/taler-util";
|
||||
@ -37,6 +37,9 @@ import {
|
||||
import { useTalerActionURL } from "./hooks/useTalerActionURL";
|
||||
import { strings } from "./i18n/strings";
|
||||
import { Pages, PopupNavBar } from "./NavigationBar";
|
||||
import { platform, setupPlatform } from "./platform/api";
|
||||
import chromeAPI from "./platform/chrome";
|
||||
import firefoxAPI from "./platform/firefox";
|
||||
import { BalancePage } from "./popup/BalancePage";
|
||||
import { TalerActionFound } from "./popup/TalerActionFound";
|
||||
import { BackupPage } from "./wallet/BackupPage";
|
||||
@ -59,6 +62,17 @@ function main(): void {
|
||||
|
||||
setupI18n("en", strings);
|
||||
|
||||
//FIXME: create different entry point for any platform instead of
|
||||
//switching in runtime
|
||||
const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
|
||||
if (isFirefox) {
|
||||
console.log("Wallet setup for Firefox API");
|
||||
setupPlatform(firefoxAPI);
|
||||
} else {
|
||||
console.log("Wallet setup for Chrome API");
|
||||
setupPlatform(chromeAPI);
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", main);
|
||||
} else {
|
||||
@ -106,7 +120,7 @@ function Application(): VNode {
|
||||
route(Pages.balance_deposit.replace(":currency", currency))
|
||||
}
|
||||
goToWalletHistory={(currency: string) =>
|
||||
route(Pages.balance_history.replace(":currency", currency))
|
||||
route(Pages.balance_history.replace(":currency?", currency))
|
||||
}
|
||||
/>
|
||||
|
||||
@ -180,19 +194,10 @@ function Application(): VNode {
|
||||
}
|
||||
|
||||
function RedirectToWalletPage(): VNode {
|
||||
const page = document.location.hash || "#/";
|
||||
const page = (document.location.hash || "#/").replace("#", "");
|
||||
const [showText, setShowText] = useState(false);
|
||||
useEffect(() => {
|
||||
chrome.tabs.create(
|
||||
{
|
||||
active: true,
|
||||
// eslint-disable-next-line no-undef
|
||||
url: chrome.runtime.getURL(`/static/wallet.html${page}`),
|
||||
},
|
||||
() => {
|
||||
window.close();
|
||||
},
|
||||
);
|
||||
platform.openWalletPageFromPopup(page);
|
||||
setTimeout(() => {
|
||||
setShowText(true);
|
||||
}, 250);
|
||||
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helpers functions to render Taler-related data structures to HTML.
|
||||
*
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
amountFractionalBase,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Component, ComponentChildren, h, VNode } from "preact";
|
||||
|
||||
/**
|
||||
* Render amount as HTML, which non-breaking space between
|
||||
* decimal value and currency.
|
||||
*/
|
||||
export function renderAmount(amount: AmountJson | string): VNode {
|
||||
let a;
|
||||
if (typeof amount === "string") {
|
||||
a = Amounts.parse(amount);
|
||||
} else {
|
||||
a = amount;
|
||||
}
|
||||
if (!a) {
|
||||
return <span>(invalid amount)</span>;
|
||||
}
|
||||
const x = a.value + a.fraction / amountFractionalBase;
|
||||
return (
|
||||
<span>
|
||||
{x} {a.currency}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export const AmountView = ({
|
||||
amount,
|
||||
}: {
|
||||
amount: AmountJson | string;
|
||||
}): VNode => renderAmount(amount);
|
||||
|
||||
/**
|
||||
* Abbreviate a string to a given length, and show the full
|
||||
* string on hover as a tooltip.
|
||||
*/
|
||||
export function abbrev(s: string, n = 5): VNode {
|
||||
let sAbbrev = s;
|
||||
if (s.length > n) {
|
||||
sAbbrev = s.slice(0, n) + "..";
|
||||
}
|
||||
return (
|
||||
<span class="abbrev" title={s}>
|
||||
{sAbbrev}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
interface CollapsibleState {
|
||||
collapsed: boolean;
|
||||
}
|
||||
|
||||
interface CollapsibleProps {
|
||||
initiallyCollapsed: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that shows/hides its children when clicking
|
||||
* a heading.
|
||||
*/
|
||||
export class Collapsible extends Component<CollapsibleProps, CollapsibleState> {
|
||||
constructor(props: CollapsibleProps) {
|
||||
super(props);
|
||||
this.state = { collapsed: props.initiallyCollapsed };
|
||||
}
|
||||
render(): VNode {
|
||||
const doOpen = (e: any): void => {
|
||||
this.setState({ collapsed: false });
|
||||
e.preventDefault();
|
||||
};
|
||||
const doClose = (e: any): void => {
|
||||
this.setState({ collapsed: true });
|
||||
e.preventDefault();
|
||||
};
|
||||
if (this.state.collapsed) {
|
||||
return (
|
||||
<h2>
|
||||
<a class="opener opener-collapsed" href="#" onClick={doOpen}>
|
||||
{" "}
|
||||
{this.props.title}
|
||||
</a>
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h2>
|
||||
<a class="opener opener-open" href="#" onClick={doClose}>
|
||||
{" "}
|
||||
{this.props.title}
|
||||
</a>
|
||||
</h2>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface ExpanderTextProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a heading with a toggle to show/hide the expandable content.
|
||||
*/
|
||||
export function ExpanderText({ text }: ExpanderTextProps): VNode {
|
||||
return <span>{text}</span>;
|
||||
}
|
||||
|
||||
export interface LoadingButtonProps
|
||||
extends h.JSX.HTMLAttributes<HTMLButtonElement> {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export function ProgressButton({
|
||||
isLoading,
|
||||
...rest
|
||||
}: LoadingButtonProps): VNode {
|
||||
return (
|
||||
<button class="pure-button pure-button-primary" type="button" {...rest}>
|
||||
{isLoading ? (
|
||||
<span>
|
||||
<object class="svg-icon svg-baseline" data="/img/spinner-bars.svg" />
|
||||
</span>
|
||||
) : null}{" "}
|
||||
{rest.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function PageLink(props: {
|
||||
pageName: string;
|
||||
children?: ComponentChildren;
|
||||
}): VNode {
|
||||
// eslint-disable-next-line no-undef
|
||||
|
||||
const url =
|
||||
typeof chrome === "undefined"
|
||||
? undefined
|
||||
: // eslint-disable-next-line no-undef
|
||||
chrome.runtime?.getURL(`/static/wallet.html#/${props.pageName}`);
|
||||
return (
|
||||
<a class="actionLink" href={url} target="_blank" rel="noopener noreferrer">
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
}
|
@ -187,52 +187,3 @@ export function amountToString(text: AmountJson): string {
|
||||
return `${amount} ${aj.currency}`;
|
||||
}
|
||||
|
||||
export function actionForTalerUri(
|
||||
uriType: TalerUriType,
|
||||
talerUri: string,
|
||||
): string | undefined {
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return makeExtensionUrlWithParams("static/wallet.html#/cta/withdraw", {
|
||||
talerWithdrawUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerPay:
|
||||
return makeExtensionUrlWithParams("static/wallet.html#/cta/pay", {
|
||||
talerPayUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerTip:
|
||||
return makeExtensionUrlWithParams("static/wallet.html#/cta/tip", {
|
||||
talerTipUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerRefund:
|
||||
return makeExtensionUrlWithParams("static/wallet.html#/cta/refund", {
|
||||
talerRefundUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerNotifyReserve:
|
||||
// FIXME: implement
|
||||
break;
|
||||
default:
|
||||
console.warn(
|
||||
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
|
||||
);
|
||||
break;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function makeExtensionUrlWithParams(
|
||||
url: string,
|
||||
params?: { [name: string]: string | undefined },
|
||||
): string {
|
||||
// eslint-disable-next-line no-undef
|
||||
const innerUrl = new URL(chrome.runtime.getURL("/" + url));
|
||||
if (params) {
|
||||
const hParams = Object.keys(params)
|
||||
.map((k) => `${k}=${params[k]}`)
|
||||
.join("&");
|
||||
innerUrl.hash = innerUrl.hash + "?" + hParams;
|
||||
}
|
||||
return innerUrl.href;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { platform } from "../platform/api";
|
||||
import { Button, ButtonSuccess, InputWithLabel } from "../components/styled";
|
||||
import { useTranslationContext } from "../context/translation";
|
||||
import { actionForTalerUri } from "../utils/index";
|
||||
|
||||
export interface Props {
|
||||
onCancel: () => void;
|
||||
@ -14,6 +14,10 @@ export function AddNewActionView({ onCancel }: Props): VNode {
|
||||
const uriType = classifyTalerUri(url);
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
function redirectToWallet() {
|
||||
platform.openWalletURIFromPopup(uriType, url);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<section>
|
||||
@ -37,12 +41,7 @@ export function AddNewActionView({ onCancel }: Props): VNode {
|
||||
<i18n.Translate>Cancel</i18n.Translate>
|
||||
</Button>
|
||||
{uriType !== TalerUriType.Unknown && (
|
||||
<ButtonSuccess
|
||||
onClick={() => {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.tabs.create({ url: actionForTalerUri(uriType, url) });
|
||||
}}
|
||||
>
|
||||
<ButtonSuccess onClick={redirectToWallet}>
|
||||
{(() => {
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerNotifyReserve:
|
||||
|
@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Main entry point for extension pages.
|
||||
*
|
||||
* @author sebasjm <dold@taler.net>
|
||||
* @author sebasjm
|
||||
*/
|
||||
|
||||
import { setupI18n } from "@gnu-taler/taler-util";
|
||||
@ -28,12 +28,7 @@ import Match from "preact-router/match";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { LogoHeader } from "./components/LogoHeader";
|
||||
import PendingTransactions from "./components/PendingTransactions";
|
||||
import {
|
||||
NavigationHeader,
|
||||
NavigationHeaderHolder,
|
||||
SuccessBox,
|
||||
WalletBox,
|
||||
} from "./components/styled";
|
||||
import { SuccessBox, WalletBox } from "./components/styled";
|
||||
import { DevContextProvider } from "./context/devContext";
|
||||
import { IoCProviderForRuntime } from "./context/iocContext";
|
||||
import {
|
||||
@ -46,6 +41,9 @@ import { TipPage } from "./cta/Tip";
|
||||
import { WithdrawPage } from "./cta/Withdraw";
|
||||
import { strings } from "./i18n/strings";
|
||||
import { Pages, WalletNavBar } from "./NavigationBar";
|
||||
import { setupPlatform } from "./platform/api";
|
||||
import chromeAPI from "./platform/chrome";
|
||||
import firefoxAPI from "./platform/firefox";
|
||||
import { DeveloperPage } from "./popup/DeveloperPage";
|
||||
import { BackupPage } from "./wallet/BackupPage";
|
||||
import { DepositPage } from "./wallet/DepositPage";
|
||||
@ -75,6 +73,17 @@ function main(): void {
|
||||
|
||||
setupI18n("en", strings);
|
||||
|
||||
const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
|
||||
//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 {
|
||||
console.log("Wallet setup for Chrome API");
|
||||
setupPlatform(chromeAPI);
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", main);
|
||||
} else {
|
||||
|
@ -61,7 +61,7 @@ import {
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
|
||||
import type { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw";
|
||||
import { MessageFromBackend } from "./wxBackend";
|
||||
import { platform, MessageFromBackend } from "./platform/api";
|
||||
|
||||
/**
|
||||
*
|
||||
@ -95,27 +95,18 @@ export interface UpgradeResponse {
|
||||
}
|
||||
|
||||
async function callBackend(operation: string, payload: any): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (chrome.runtime.lastError) {
|
||||
console.log("Error calling backend");
|
||||
reject(
|
||||
new Error(
|
||||
`Error contacting backend: ${chrome.runtime.lastError.message}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
console.log("got response", resp);
|
||||
const r = resp as CoreApiResponse;
|
||||
if (r.type === "error") {
|
||||
reject(TalerError.fromUncheckedDetail(r.error));
|
||||
return;
|
||||
}
|
||||
resolve(r.result);
|
||||
});
|
||||
});
|
||||
let response: CoreApiResponse;
|
||||
try {
|
||||
response = await platform.setMessageToWalletBackground(operation, payload)
|
||||
} catch (e) {
|
||||
console.log("Error calling backend");
|
||||
throw new Error(`Error contacting backend: ${e}`)
|
||||
}
|
||||
console.log("got response", response);
|
||||
if (response.type === "error") {
|
||||
throw new TalerError.fromUncheckedDetail(response.error);
|
||||
}
|
||||
return response.result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -422,20 +413,12 @@ export function importDB(dump: any): Promise<void> {
|
||||
return callBackend("importDb", { dump });
|
||||
}
|
||||
|
||||
export function onUpdateNotification(
|
||||
messageTypes: Array<NotificationType>,
|
||||
doCallback: () => void,
|
||||
): () => void {
|
||||
// eslint-disable-next-line no-undef
|
||||
const port = chrome.runtime.connect({ name: "notifications" });
|
||||
export function onUpdateNotification(messageTypes: Array<NotificationType>, doCallback: () => void): () => void {
|
||||
const listener = (message: MessageFromBackend): void => {
|
||||
const shouldNotify = messageTypes.includes(message.type);
|
||||
if (shouldNotify) {
|
||||
doCallback();
|
||||
}
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
return () => {
|
||||
port.onMessage.removeListener(listener);
|
||||
};
|
||||
return platform.listenToWalletNotifications(listener)
|
||||
}
|
||||
|
@ -26,11 +26,9 @@
|
||||
import {
|
||||
classifyTalerUri,
|
||||
CoreApiResponse,
|
||||
CoreApiResponseSuccess,
|
||||
NotificationType,
|
||||
TalerErrorCode,
|
||||
CoreApiResponseSuccess, TalerErrorCode,
|
||||
TalerUriType,
|
||||
WalletDiagnostics,
|
||||
WalletDiagnostics
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
DbAccess,
|
||||
@ -40,13 +38,13 @@ import {
|
||||
openPromise,
|
||||
openTalerDatabase,
|
||||
Wallet,
|
||||
WalletStoresV1,
|
||||
WalletStoresV1
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
||||
import { BrowserHttpLib } from "./browserHttpLib";
|
||||
import { getPermissionsApi, isFirefox } from "./compat";
|
||||
import { getReadRequestPermissions } from "./permissions";
|
||||
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory.js";
|
||||
import { MessageFromBackend, platform } from "./platform/api";
|
||||
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory";
|
||||
import { ServiceWorkerHttpLib } from "./serviceWorkerHttpLib";
|
||||
|
||||
/**
|
||||
@ -66,10 +64,8 @@ let outdatedDbVersion: number | undefined;
|
||||
|
||||
const walletInit: OpenedPromise<void> = openPromise<void>();
|
||||
|
||||
const notificationPorts: chrome.runtime.Port[] = [];
|
||||
|
||||
async function getDiagnostics(): Promise<WalletDiagnostics> {
|
||||
const manifestData = chrome.runtime.getManifest();
|
||||
const manifestData = platform.getWalletVersion();
|
||||
const errors: string[] = [];
|
||||
let firefoxIdbProblem = false;
|
||||
let dbOutdated = false;
|
||||
@ -80,7 +76,7 @@ async function getDiagnostics(): Promise<WalletDiagnostics> {
|
||||
if (
|
||||
currentDatabase === undefined &&
|
||||
outdatedDbVersion === undefined &&
|
||||
isFirefox()
|
||||
platform.isFirefox()
|
||||
) {
|
||||
firefoxIdbProblem = true;
|
||||
}
|
||||
@ -132,14 +128,7 @@ async function dispatch(
|
||||
break;
|
||||
}
|
||||
case "wxGetExtendedPermissions": {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
getPermissionsApi().contains(
|
||||
getReadRequestPermissions(),
|
||||
(result: boolean) => {
|
||||
resolve(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
const res = await platform.getPermissionsApi().contains(getReadRequestPermissions());
|
||||
r = wrapResponse({ newValue: res });
|
||||
break;
|
||||
}
|
||||
@ -147,15 +136,11 @@ async function dispatch(
|
||||
const newVal = req.payload.value;
|
||||
console.log("new extended permissions value", newVal);
|
||||
if (newVal) {
|
||||
setupHeaderListener();
|
||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
||||
r = wrapResponse({ newValue: true });
|
||||
} else {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
getPermissionsApi().remove(getReadRequestPermissions(), (rem) => {
|
||||
console.log("permissions removed:", rem);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
const rem = await platform.getPermissionsApi().remove(getReadRequestPermissions());
|
||||
console.log("permissions removed:", rem);
|
||||
r = wrapResponse({ newVal: false });
|
||||
}
|
||||
break;
|
||||
@ -187,74 +172,13 @@ async function dispatch(
|
||||
}
|
||||
}
|
||||
|
||||
function getTab(tabId: number): Promise<chrome.tabs.Tab> {
|
||||
return new Promise((resolve, reject) => {
|
||||
chrome.tabs.get(tabId, (tab: chrome.tabs.Tab) => resolve(tab));
|
||||
});
|
||||
}
|
||||
|
||||
function setBadgeText(options: chrome.action.BadgeTextDetails): void {
|
||||
// not supported by all browsers ...
|
||||
if (chrome && chrome.action && chrome.action.setBadgeText) {
|
||||
chrome.action.setBadgeText(options);
|
||||
} else {
|
||||
console.warn("can't set badge text, not supported", options);
|
||||
}
|
||||
}
|
||||
|
||||
function waitMs(timeoutMs: number): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const bgPage = chrome.extension.getBackgroundPage();
|
||||
if (!bgPage) {
|
||||
reject("fatal: no background page");
|
||||
return;
|
||||
}
|
||||
bgPage.setTimeout(() => resolve(), timeoutMs);
|
||||
});
|
||||
}
|
||||
|
||||
function makeSyncWalletRedirect(
|
||||
url: string,
|
||||
tabId: number,
|
||||
oldUrl: string,
|
||||
params?: { [name: string]: string | undefined },
|
||||
): Record<string, unknown> {
|
||||
const innerUrl = new URL(chrome.runtime.getURL(url));
|
||||
if (params) {
|
||||
const hParams = Object.keys(params)
|
||||
.map((k) => `${k}=${params[k]}`)
|
||||
.join("&");
|
||||
innerUrl.hash = innerUrl.hash + "?" + hParams;
|
||||
}
|
||||
// Some platforms don't support the sync redirect (yet), so fall back to
|
||||
// async redirect after a timeout.
|
||||
const doit = async (): Promise<void> => {
|
||||
await waitMs(150);
|
||||
const tab = await getTab(tabId);
|
||||
if (tab.url === oldUrl) {
|
||||
console.log("redirecting to", innerUrl.href);
|
||||
chrome.tabs.update(tabId, {
|
||||
url: innerUrl.href,
|
||||
loadReplace: true,
|
||||
} as any);
|
||||
}
|
||||
};
|
||||
doit();
|
||||
|
||||
return { redirectUrl: innerUrl.href };
|
||||
}
|
||||
|
||||
export type MessageFromBackend = {
|
||||
type: NotificationType;
|
||||
};
|
||||
|
||||
async function reinitWallet(): Promise<void> {
|
||||
if (currentWallet) {
|
||||
currentWallet.stop();
|
||||
currentWallet = undefined;
|
||||
}
|
||||
currentDatabase = undefined;
|
||||
setBadgeText({ text: "" });
|
||||
// setBadgeText({ text: "" });
|
||||
try {
|
||||
currentDatabase = await openTalerDatabase(indexedDB as any, reinitWallet);
|
||||
} catch (e) {
|
||||
@ -265,7 +189,7 @@ async function reinitWallet(): Promise<void> {
|
||||
let httpLib;
|
||||
let cryptoWorker;
|
||||
|
||||
if (chrome.runtime.getManifest().manifest_version === 3) {
|
||||
if (platform.useServiceWorkerAsBackgroundProcess()) {
|
||||
httpLib = new ServiceWorkerHttpLib();
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactory();
|
||||
} else {
|
||||
@ -283,14 +207,8 @@ async function reinitWallet(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
wallet.addNotificationListener((x) => {
|
||||
for (const notif of notificationPorts) {
|
||||
const message: MessageFromBackend = { type: x.type };
|
||||
try {
|
||||
notif.postMessage(message);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
const message: MessageFromBackend = { type: x.type };
|
||||
platform.sendMessageToAllChannels(message)
|
||||
});
|
||||
wallet.runTaskLoop().catch((e) => {
|
||||
console.log("error during wallet task loop", e);
|
||||
@ -303,135 +221,41 @@ async function reinitWallet(): Promise<void> {
|
||||
walletInit.resolve();
|
||||
}
|
||||
|
||||
try {
|
||||
// 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 === "install") {
|
||||
const url = chrome.runtime.getURL("/static/wallet.html#/welcome");
|
||||
chrome.tabs.create({ active: true, url });
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
function parseTalerUriAndRedirect(tabId: number, talerUri: string) {
|
||||
const uriType = classifyTalerUri(talerUri);
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return platform.redirectTabToWalletPage(
|
||||
tabId,
|
||||
`/cta/withdraw?talerWithdrawUri=${talerUri}`,
|
||||
);
|
||||
case TalerUriType.TalerPay:
|
||||
return platform.redirectTabToWalletPage(
|
||||
tabId,
|
||||
`/cta/pay?talerPayUri=${talerUri}`,
|
||||
);
|
||||
case TalerUriType.TalerTip:
|
||||
return platform.redirectTabToWalletPage(
|
||||
tabId,
|
||||
`/cta/tip?talerTipUri=${talerUri}`,
|
||||
);
|
||||
case TalerUriType.TalerRefund:
|
||||
return platform.redirectTabToWalletPage(
|
||||
tabId,
|
||||
`/cta/refund?talerRefundUri=${talerUri}`,
|
||||
);
|
||||
case TalerUriType.TalerNotifyReserve:
|
||||
// FIXME: Is this still useful?
|
||||
// handleNotifyReserve(w);
|
||||
break;
|
||||
default:
|
||||
console.warn(
|
||||
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function headerListener(
|
||||
details: chrome.webRequest.WebResponseHeadersDetails,
|
||||
): chrome.webRequest.BlockingResponse | undefined {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(chrome.runtime.lastError);
|
||||
return;
|
||||
}
|
||||
const wallet = currentWallet;
|
||||
if (!wallet) {
|
||||
console.warn("wallet not available while handling header");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
details.statusCode === 402 ||
|
||||
details.statusCode === 202 ||
|
||||
details.statusCode === 200
|
||||
) {
|
||||
for (const header of details.responseHeaders || []) {
|
||||
if (header.name.toLowerCase() === "taler") {
|
||||
const talerUri = header.value || "";
|
||||
const uriType = classifyTalerUri(talerUri);
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/wallet.html#/cta/withdraw",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
talerWithdrawUri: talerUri,
|
||||
},
|
||||
);
|
||||
case TalerUriType.TalerPay:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/wallet.html#/cta/pay",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
talerPayUri: talerUri,
|
||||
},
|
||||
);
|
||||
case TalerUriType.TalerTip:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/wallet.html#/cta/tip",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
talerTipUri: talerUri,
|
||||
},
|
||||
);
|
||||
case TalerUriType.TalerRefund:
|
||||
return makeSyncWalletRedirect(
|
||||
"/static/wallet.html#/cta/refund",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
talerRefundUri: talerUri,
|
||||
},
|
||||
);
|
||||
case TalerUriType.TalerNotifyReserve:
|
||||
Promise.resolve().then(() => {
|
||||
const w = currentWallet;
|
||||
if (!w) {
|
||||
return;
|
||||
}
|
||||
// FIXME: Is this still useful?
|
||||
// handleNotifyReserve(w);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.warn(
|
||||
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function setupHeaderListener(): void {
|
||||
// if (chrome.runtime.getManifest().manifest_version === 3) {
|
||||
// console.error("cannot block request on manfest v3")
|
||||
// return
|
||||
// }
|
||||
console.log("setting up header listener");
|
||||
// Handlers for catching HTTP requests
|
||||
getPermissionsApi().contains(
|
||||
getReadRequestPermissions(),
|
||||
(result: boolean) => {
|
||||
if (
|
||||
"webRequest" in chrome &&
|
||||
"onHeadersReceived" in chrome.webRequest &&
|
||||
chrome.webRequest.onHeadersReceived.hasListener(headerListener)
|
||||
) {
|
||||
chrome.webRequest.onHeadersReceived.removeListener(headerListener);
|
||||
}
|
||||
if (result) {
|
||||
console.log("actually adding listener");
|
||||
chrome.webRequest.onHeadersReceived.addListener(
|
||||
headerListener,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["responseHeaders"],
|
||||
);
|
||||
}
|
||||
if ("webRequest" in chrome) {
|
||||
chrome.webRequest.handlerBehaviorChanged(() => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(chrome.runtime.lastError);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to run for the WebExtension backend.
|
||||
@ -439,48 +263,34 @@ function setupHeaderListener(): void {
|
||||
* Sets up all event handlers and other machinery.
|
||||
*/
|
||||
export async function wxMain(): Promise<void> {
|
||||
// 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();
|
||||
});
|
||||
const afterWalletIsInitialized = reinitWallet();
|
||||
|
||||
platform.registerReloadOnNewVersion();
|
||||
|
||||
// Handlers for messages coming directly from the content
|
||||
// script on the page
|
||||
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
|
||||
platform.registerOnNewMessage((message, sender, callback) => {
|
||||
afterWalletIsInitialized.then(() => {
|
||||
dispatch(req, sender, sendResponse);
|
||||
dispatch(message, sender, callback);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
})
|
||||
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
notificationPorts.push(port);
|
||||
port.onDisconnect.addListener((discoPort) => {
|
||||
const idx = notificationPorts.indexOf(discoPort);
|
||||
if (idx >= 0) {
|
||||
notificationPorts.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
platform.registerAllIncomingConnections()
|
||||
|
||||
try {
|
||||
if (chrome.runtime.getManifest().manifest_version === 2) {
|
||||
setupHeaderListener();
|
||||
}
|
||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
// On platforms that support it, also listen to external
|
||||
// modification of permissions.
|
||||
getPermissionsApi().addPermissionsListener((perm) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error(chrome.runtime.lastError);
|
||||
platform.getPermissionsApi().addPermissionsListener((perm) => {
|
||||
const lastError = platform.getLastError()
|
||||
if (lastError) {
|
||||
console.error(lastError);
|
||||
return;
|
||||
}
|
||||
setupHeaderListener();
|
||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user