check if the script should be injected
remove header listener
This commit is contained in:
parent
b1a0d034fc
commit
b34f3568e8
@ -17,8 +17,7 @@
|
|||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"unlimitedStorage",
|
"unlimitedStorage",
|
||||||
"http://*/*",
|
"storage",
|
||||||
"https://*/*",
|
|
||||||
"activeTab"
|
"activeTab"
|
||||||
],
|
],
|
||||||
"web_accessible_resources": [
|
"web_accessible_resources": [
|
||||||
@ -28,21 +27,31 @@
|
|||||||
"dist/taler-wallet-interaction-support.js.map",
|
"dist/taler-wallet-interaction-support.js.map",
|
||||||
"dist/taler-wallet-interaction-support.js"
|
"dist/taler-wallet-interaction-support.js"
|
||||||
],
|
],
|
||||||
"optional_permissions": [
|
|
||||||
"http://*/*",
|
|
||||||
"https://*/*",
|
|
||||||
"webRequest"
|
|
||||||
],
|
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
"id": "taler-wallet-interaction-support",
|
"id": "taler-wallet-interaction-support",
|
||||||
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
||||||
"js": ["dist/taler-wallet-interaction-loader.js"]
|
"js": ["dist/taler-wallet-interaction-loader.js"]
|
||||||
}],
|
}],
|
||||||
"protocol_handlers": [
|
"protocol_handlers": [
|
||||||
|
{
|
||||||
|
"protocol": "ext+taler+http",
|
||||||
|
"name": "Taler Wallet WebExtension",
|
||||||
|
"uriTemplate": "/static/wallet.html#/cta/taler-uri/%s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "web+taler+http",
|
||||||
|
"name": "Taler Wallet WebExtension",
|
||||||
|
"uriTemplate": "/static/wallet.html#/cta/taler-uri/%s"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"protocol": "ext+taler",
|
"protocol": "ext+taler",
|
||||||
"name": "Taler Wallet WebExtension",
|
"name": "Taler Wallet WebExtension",
|
||||||
"uriTemplate": "/static/wallet.html#/cta/withdraw?d=1&talerWithdrawUri=%s"
|
"uriTemplate": "/static/wallet.html#/cta/taler-uri/%s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"protocol": "web+taler",
|
||||||
|
"name": "Taler Wallet WebExtension",
|
||||||
|
"uriTemplate": "/static/wallet.html#/cta/taler-uri/%s"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"unlimitedStorage",
|
"unlimitedStorage",
|
||||||
|
"storage",
|
||||||
"activeTab",
|
"activeTab",
|
||||||
"scripting",
|
"scripting",
|
||||||
"declarativeContent",
|
"declarativeContent",
|
||||||
@ -26,9 +27,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"optional_permissions": [
|
|
||||||
"webRequest"
|
|
||||||
],
|
|
||||||
"content_scripts": [{
|
"content_scripts": [{
|
||||||
"id": "taler-wallet-interaction",
|
"id": "taler-wallet-interaction",
|
||||||
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
"matches": ["file://*/*", "http://*/*", "https://*/*"],
|
||||||
@ -50,10 +48,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"host_permissions": [
|
|
||||||
"http://*/*",
|
|
||||||
"https://*/*"
|
|
||||||
],
|
|
||||||
"action": {
|
"action": {
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
"16": "static/img/taler-logo-16.png",
|
"16": "static/img/taler-logo-16.png",
|
||||||
|
@ -30,14 +30,8 @@ import { wxMain } from "./wxBackend.js";
|
|||||||
console.log("Wallet setup for Dev API");
|
console.log("Wallet setup for Dev API");
|
||||||
setupPlatform(devAPI);
|
setupPlatform(devAPI);
|
||||||
|
|
||||||
try {
|
async function start() {
|
||||||
platform.registerOnInstalled(() => {
|
await platform.notifyWhenAppIsReady();
|
||||||
platform.openWalletPage("/welcome");
|
await wxMain();
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
|
start();
|
||||||
platform.notifyWhenAppIsReady(() => {
|
|
||||||
wxMain();
|
|
||||||
});
|
|
||||||
|
@ -43,6 +43,9 @@ if (isFirefox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setGlobalLogLevelFromString("trace")
|
// setGlobalLogLevelFromString("trace")
|
||||||
platform.notifyWhenAppIsReady(() => {
|
|
||||||
wxMain();
|
async function start() {
|
||||||
});
|
await platform.notifyWhenAppIsReady();
|
||||||
|
await wxMain();
|
||||||
|
}
|
||||||
|
start();
|
||||||
|
@ -20,6 +20,12 @@ import { useBackendContext } from "../context/backend.js";
|
|||||||
import { ToggleHandler } from "../mui/handlers.js";
|
import { ToggleHandler } from "../mui/handlers.js";
|
||||||
import { platform } from "../platform/foreground.js";
|
import { platform } from "../platform/foreground.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is not implemented.
|
||||||
|
* Clipboard permission need to get ask the permission to the user
|
||||||
|
* based on user-intention
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
export function useClipboardPermissions(): ToggleHandler {
|
export function useClipboardPermissions(): ToggleHandler {
|
||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
const api = useBackendContext();
|
const api = useBackendContext();
|
||||||
@ -40,27 +46,27 @@ export function useClipboardPermissions(): ToggleHandler {
|
|||||||
}
|
}
|
||||||
setEnabled(granted);
|
setEnabled(granted);
|
||||||
} else {
|
} else {
|
||||||
try {
|
// try {
|
||||||
await api.background
|
// await api.background
|
||||||
.call("toggleHeaderListener", false)
|
// .call("toggleHeaderListener", false)
|
||||||
.then((r) => setEnabled(r.newValue));
|
// .then((r) => setEnabled(r.newValue));
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
console.log(e);
|
// console.log(e);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
async function getValue(): Promise<void> {
|
// async function getValue(): Promise<void> {
|
||||||
const res = await api.background.call(
|
// const res = await api.background.call(
|
||||||
"containsHeaderListener",
|
// "containsHeaderListener",
|
||||||
undefined,
|
// undefined,
|
||||||
);
|
// );
|
||||||
setEnabled(res.newValue);
|
// setEnabled(res.newValue);
|
||||||
}
|
// }
|
||||||
getValue();
|
// getValue();
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: enabled,
|
value: enabled,
|
||||||
|
@ -15,14 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useLocalStorage } from "@gnu-taler/web-util/lib/index.browser";
|
import { useLocalStorage } from "@gnu-taler/web-util/lib/index.browser";
|
||||||
|
import { Settings, defaultSettings } from "../platform/api.js";
|
||||||
interface Settings {
|
|
||||||
injectTalerSupport: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultSettings: Settings = {
|
|
||||||
injectTalerSupport: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
function parse_json_or_undefined<T>(str: string | undefined): T | undefined {
|
function parse_json_or_undefined<T>(str: string | undefined): T | undefined {
|
||||||
if (str === undefined) return undefined;
|
if (str === undefined) return undefined;
|
||||||
@ -42,7 +35,6 @@ export function useSettings(): [
|
|||||||
const parsed: Settings = parse_json_or_undefined(value) ?? defaultSettings;
|
const parsed: Settings = parse_json_or_undefined(value) ?? defaultSettings;
|
||||||
function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
|
function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
|
||||||
const newValue = { ...parsed, [k]: v };
|
const newValue = { ...parsed, [k]: v };
|
||||||
console.log("should update", k, v, parsed, newValue);
|
|
||||||
const json = JSON.stringify(newValue);
|
const json = JSON.stringify(newValue);
|
||||||
console.log(json);
|
console.log(json);
|
||||||
update(json);
|
update(json);
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
import { CoreApiResponse, NotificationType } from "@gnu-taler/taler-util";
|
import { CoreApiResponse, NotificationType } from "@gnu-taler/taler-util";
|
||||||
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
|
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
|
||||||
import { BackgroundOperations } from "../wxApi.js";
|
import { BackgroundOperations } from "../wxApi.js";
|
||||||
|
import {
|
||||||
|
ExtensionOperations,
|
||||||
|
MessageFromExtension,
|
||||||
|
} from "../taler-wallet-interaction-loader.js";
|
||||||
|
|
||||||
export interface Permissions {
|
export interface Permissions {
|
||||||
/**
|
/**
|
||||||
@ -35,9 +39,9 @@ export interface Permissions {
|
|||||||
* Compatibility API that works on multiple browsers.
|
* Compatibility API that works on multiple browsers.
|
||||||
*/
|
*/
|
||||||
export interface CrossBrowserPermissionsApi {
|
export interface CrossBrowserPermissionsApi {
|
||||||
containsHostPermissions(): Promise<boolean>;
|
// containsHostPermissions(): Promise<boolean>;
|
||||||
requestHostPermissions(): Promise<boolean>;
|
// requestHostPermissions(): Promise<boolean>;
|
||||||
removeHostPermissions(): Promise<boolean>;
|
// removeHostPermissions(): Promise<boolean>;
|
||||||
|
|
||||||
containsClipboardPermissions(): Promise<boolean>;
|
containsClipboardPermissions(): Promise<boolean>;
|
||||||
requestClipboardPermissions(): Promise<boolean>;
|
requestClipboardPermissions(): Promise<boolean>;
|
||||||
@ -53,9 +57,11 @@ export type MessageFromBackend = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type MessageFromFrontend<
|
export type MessageFromFrontend<
|
||||||
Op extends BackgroundOperations | WalletOperations,
|
Op extends BackgroundOperations | WalletOperations | ExtensionOperations,
|
||||||
> = Op extends BackgroundOperations
|
> = Op extends BackgroundOperations
|
||||||
? MessageFromFrontendBackground<keyof BackgroundOperations>
|
? MessageFromFrontendBackground<keyof BackgroundOperations>
|
||||||
|
: Op extends ExtensionOperations
|
||||||
|
? MessageFromExtension<keyof ExtensionOperations>
|
||||||
: Op extends WalletOperations
|
: Op extends WalletOperations
|
||||||
? MessageFromFrontendWallet<keyof WalletOperations>
|
? MessageFromFrontendWallet<keyof WalletOperations>
|
||||||
: never;
|
: never;
|
||||||
@ -81,11 +87,23 @@ export interface WalletWebExVersion {
|
|||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Settings {
|
||||||
|
injectTalerSupport: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultSettings: Settings = {
|
||||||
|
injectTalerSupport: false,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compatibility helpers needed for browsers that don't implement
|
* Compatibility helpers needed for browsers that don't implement
|
||||||
* WebExtension APIs consistently.
|
* WebExtension APIs consistently.
|
||||||
*/
|
*/
|
||||||
export interface BackgroundPlatformAPI {
|
export interface BackgroundPlatformAPI {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
getSettingsFromStorage(): Promise<Settings>;
|
||||||
/**
|
/**
|
||||||
* Guarantee that the service workers don't die
|
* Guarantee that the service workers don't die
|
||||||
*/
|
*/
|
||||||
@ -116,16 +134,12 @@ export interface BackgroundPlatformAPI {
|
|||||||
* Register a callback to be called when the wallet is ready to start
|
* Register a callback to be called when the wallet is ready to start
|
||||||
* @param callback
|
* @param callback
|
||||||
*/
|
*/
|
||||||
notifyWhenAppIsReady(callback: () => void): void;
|
notifyWhenAppIsReady(): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the wallet version from manifest
|
* Get the wallet version from manifest
|
||||||
*/
|
*/
|
||||||
getWalletWebExVersion(): WalletWebExVersion;
|
getWalletWebExVersion(): WalletWebExVersion;
|
||||||
/**
|
|
||||||
* Frontend API
|
|
||||||
*/
|
|
||||||
containsTalerHeaderListener(): boolean;
|
|
||||||
/**
|
/**
|
||||||
* Backend API
|
* Backend API
|
||||||
*/
|
*/
|
||||||
@ -134,12 +148,6 @@ export interface BackgroundPlatformAPI {
|
|||||||
* Backend API
|
* Backend API
|
||||||
*/
|
*/
|
||||||
registerReloadOnNewVersion(): void;
|
registerReloadOnNewVersion(): void;
|
||||||
/**
|
|
||||||
* Backend API
|
|
||||||
*/
|
|
||||||
registerTalerHeaderListener(
|
|
||||||
onHeader: (tabId: number, url: string) => void,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permission API for checking and add a listener
|
* Permission API for checking and add a listener
|
||||||
|
@ -31,10 +31,13 @@ import {
|
|||||||
MessageFromFrontend,
|
MessageFromFrontend,
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
Permissions,
|
Permissions,
|
||||||
|
Settings,
|
||||||
|
defaultSettings,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
|
|
||||||
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
||||||
isFirefox,
|
isFirefox,
|
||||||
|
getSettingsFromStorage,
|
||||||
findTalerUriInActiveTab,
|
findTalerUriInActiveTab,
|
||||||
findTalerUriInClipboard,
|
findTalerUriInClipboard,
|
||||||
getPermissionsApi,
|
getPermissionsApi,
|
||||||
@ -49,11 +52,9 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
|||||||
registerOnInstalled,
|
registerOnInstalled,
|
||||||
listenToAllChannels: listenToAllChannels as any,
|
listenToAllChannels: listenToAllChannels as any,
|
||||||
registerReloadOnNewVersion,
|
registerReloadOnNewVersion,
|
||||||
registerTalerHeaderListener,
|
|
||||||
sendMessageToAllChannels,
|
sendMessageToAllChannels,
|
||||||
sendMessageToBackground,
|
sendMessageToBackground,
|
||||||
useServiceWorkerAsBackgroundProcess,
|
useServiceWorkerAsBackgroundProcess,
|
||||||
containsTalerHeaderListener,
|
|
||||||
keepAlive,
|
keepAlive,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,6 +62,19 @@ export default api;
|
|||||||
|
|
||||||
const logger = new Logger("chrome.ts");
|
const logger = new Logger("chrome.ts");
|
||||||
|
|
||||||
|
async function getSettingsFromStorage(): Promise<Settings> {
|
||||||
|
const data = await chrome.storage.local.get("wallet-settings");
|
||||||
|
if (!data) return defaultSettings;
|
||||||
|
const settings = data["wallet-settings"];
|
||||||
|
if (!settings) return defaultSettings;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(settings);
|
||||||
|
return parsed;
|
||||||
|
} catch (e) {
|
||||||
|
return defaultSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function keepAlive(callback: any): void {
|
function keepAlive(callback: any): void {
|
||||||
if (extensionIsManifestV3()) {
|
if (extensionIsManifestV3()) {
|
||||||
chrome.alarms.create("wallet-worker", { periodInMinutes: 1 });
|
chrome.alarms.create("wallet-worker", { periodInMinutes: 1 });
|
||||||
@ -78,10 +92,10 @@ function isFirefox(): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hostPermissions = {
|
// const hostPermissions = {
|
||||||
permissions: ["webRequest"],
|
// permissions: ["webRequest"],
|
||||||
origins: ["http://*/*", "https://*/*"],
|
// origins: ["http://*/*", "https://*/*"],
|
||||||
};
|
// };
|
||||||
|
|
||||||
export function containsClipboardPermissions(): Promise<boolean> {
|
export function containsClipboardPermissions(): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
@ -96,17 +110,17 @@ export function containsClipboardPermissions(): Promise<boolean> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function containsHostPermissions(): Promise<boolean> {
|
// export function containsHostPermissions(): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
// return new Promise((res, rej) => {
|
||||||
chrome.permissions.contains(hostPermissions, (resp) => {
|
// chrome.permissions.contains(hostPermissions, (resp) => {
|
||||||
const le = chrome.runtime.lastError?.message;
|
// const le = chrome.runtime.lastError?.message;
|
||||||
if (le) {
|
// if (le) {
|
||||||
rej(le);
|
// rej(le);
|
||||||
}
|
// }
|
||||||
res(resp);
|
// res(resp);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
export async function requestClipboardPermissions(): Promise<boolean> {
|
export async function requestClipboardPermissions(): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
@ -121,73 +135,67 @@ export async function requestClipboardPermissions(): Promise<boolean> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestHostPermissions(): Promise<boolean> {
|
// export async function requestHostPermissions(): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
// return new Promise((res, rej) => {
|
||||||
chrome.permissions.request(hostPermissions, (resp) => {
|
// chrome.permissions.request(hostPermissions, (resp) => {
|
||||||
const le = chrome.runtime.lastError?.message;
|
// const le = chrome.runtime.lastError?.message;
|
||||||
if (le) {
|
// if (le) {
|
||||||
rej(le);
|
// rej(le);
|
||||||
}
|
// }
|
||||||
res(resp);
|
// res(resp);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
type HeaderListenerFunc = (
|
// type HeaderListenerFunc = (
|
||||||
details: chrome.webRequest.WebResponseHeadersDetails,
|
// details: chrome.webRequest.WebResponseHeadersDetails,
|
||||||
) => void;
|
// ) => void;
|
||||||
let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
|
// let currentHeaderListener: HeaderListenerFunc | undefined = undefined;
|
||||||
|
|
||||||
type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void;
|
// type TabListenerFunc = (tabId: number, info: chrome.tabs.TabChangeInfo) => void;
|
||||||
let currentTabListener: TabListenerFunc | undefined = undefined;
|
// let currentTabListener: TabListenerFunc | undefined = undefined;
|
||||||
|
|
||||||
export function containsTalerHeaderListener(): boolean {
|
// export async function removeHostPermissions(): Promise<boolean> {
|
||||||
return (
|
// //if there is a handler already, remove it
|
||||||
currentHeaderListener !== undefined || currentTabListener !== undefined
|
// if (
|
||||||
);
|
// currentHeaderListener &&
|
||||||
}
|
// chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener)
|
||||||
|
// ) {
|
||||||
|
// chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
|
||||||
|
// }
|
||||||
|
// if (
|
||||||
|
// currentTabListener &&
|
||||||
|
// chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
|
||||||
|
// ) {
|
||||||
|
// chrome.tabs.onUpdated.removeListener(currentTabListener);
|
||||||
|
// }
|
||||||
|
|
||||||
export async function removeHostPermissions(): Promise<boolean> {
|
// currentHeaderListener = undefined;
|
||||||
//if there is a handler already, remove it
|
// currentTabListener = undefined;
|
||||||
if (
|
|
||||||
currentHeaderListener &&
|
|
||||||
chrome?.webRequest?.onHeadersReceived?.hasListener(currentHeaderListener)
|
|
||||||
) {
|
|
||||||
chrome.webRequest.onHeadersReceived.removeListener(currentHeaderListener);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
currentTabListener &&
|
|
||||||
chrome?.tabs?.onUpdated?.hasListener(currentTabListener)
|
|
||||||
) {
|
|
||||||
chrome.tabs.onUpdated.removeListener(currentTabListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentHeaderListener = undefined;
|
// //notify the browser about this change, this operation is expensive
|
||||||
currentTabListener = undefined;
|
// if ("webRequest" in chrome) {
|
||||||
|
// chrome.webRequest.handlerBehaviorChanged(() => {
|
||||||
|
// if (chrome.runtime.lastError) {
|
||||||
|
// logger.error(JSON.stringify(chrome.runtime.lastError));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
//notify the browser about this change, this operation is expensive
|
// if (extensionIsManifestV3()) {
|
||||||
if ("webRequest" in chrome) {
|
// // Trying to remove host permissions with manifest >= v3 throws an error
|
||||||
chrome.webRequest.handlerBehaviorChanged(() => {
|
// return true;
|
||||||
if (chrome.runtime.lastError) {
|
// }
|
||||||
logger.error(JSON.stringify(chrome.runtime.lastError));
|
// return new Promise((res, rej) => {
|
||||||
}
|
// chrome.permissions.remove(hostPermissions, (resp) => {
|
||||||
});
|
// const le = chrome.runtime.lastError?.message;
|
||||||
}
|
// if (le) {
|
||||||
|
// rej(le);
|
||||||
if (extensionIsManifestV3()) {
|
// }
|
||||||
// Trying to remove host permissions with manifest >= v3 throws an error
|
// res(resp);
|
||||||
return true;
|
// });
|
||||||
}
|
// });
|
||||||
return new Promise((res, rej) => {
|
// }
|
||||||
chrome.permissions.remove(hostPermissions, (resp) => {
|
|
||||||
const le = chrome.runtime.lastError?.message;
|
|
||||||
if (le) {
|
|
||||||
rej(le);
|
|
||||||
}
|
|
||||||
res(resp);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeClipboardPermissions(): Promise<boolean> {
|
export function removeClipboardPermissions(): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
@ -214,9 +222,9 @@ function addPermissionsListener(
|
|||||||
function getPermissionsApi(): CrossBrowserPermissionsApi {
|
function getPermissionsApi(): CrossBrowserPermissionsApi {
|
||||||
return {
|
return {
|
||||||
addPermissionsListener,
|
addPermissionsListener,
|
||||||
containsHostPermissions,
|
// containsHostPermissions,
|
||||||
requestHostPermissions,
|
// requestHostPermissions,
|
||||||
removeHostPermissions,
|
// removeHostPermissions,
|
||||||
requestClipboardPermissions,
|
requestClipboardPermissions,
|
||||||
removeClipboardPermissions,
|
removeClipboardPermissions,
|
||||||
containsClipboardPermissions,
|
containsClipboardPermissions,
|
||||||
@ -227,12 +235,16 @@ function getPermissionsApi(): CrossBrowserPermissionsApi {
|
|||||||
*
|
*
|
||||||
* @param callback function to be called
|
* @param callback function to be called
|
||||||
*/
|
*/
|
||||||
function notifyWhenAppIsReady(callback: () => void): void {
|
function notifyWhenAppIsReady(): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
if (extensionIsManifestV3()) {
|
if (extensionIsManifestV3()) {
|
||||||
callback();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener("load", callback);
|
window.addEventListener("load", () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openWalletURIFromPopup(maybeTalerUri: string): void {
|
function openWalletURIFromPopup(maybeTalerUri: string): void {
|
||||||
@ -478,101 +490,6 @@ function getWalletWebExVersion(): WalletVersion {
|
|||||||
return manifestData;
|
return manifestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerTalerHeaderListener(
|
|
||||||
callback: (tabId: number, url: string) => void,
|
|
||||||
): void {
|
|
||||||
logger.trace("setting up header listener");
|
|
||||||
|
|
||||||
function headerListener(
|
|
||||||
details: chrome.webRequest.WebResponseHeadersDetails,
|
|
||||||
): void {
|
|
||||||
if (chrome.runtime.lastError) {
|
|
||||||
logger.error(JSON.stringify(chrome.runtime.lastError));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
details.statusCode === 402 ||
|
|
||||||
details.statusCode === 202 ||
|
|
||||||
details.statusCode === 200
|
|
||||||
) {
|
|
||||||
const values = (details.responseHeaders || [])
|
|
||||||
.filter((h) => h.name.toLowerCase() === "taler")
|
|
||||||
.map((h) => h.value)
|
|
||||||
.filter((value): value is string => !!value);
|
|
||||||
if (values.length > 0) {
|
|
||||||
logger.info(
|
|
||||||
`Found a Taler URI in a response header for the request ${details.url} from tab ${details.tabId}`,
|
|
||||||
);
|
|
||||||
callback(details.tabId, values[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function tabListener(
|
|
||||||
tabId: number,
|
|
||||||
info: chrome.tabs.TabChangeInfo,
|
|
||||||
): Promise<void> {
|
|
||||||
if (tabId < 0) return;
|
|
||||||
const tabLocationHasBeenUpdated = info.status === "complete";
|
|
||||||
const tabTitleHasBeenUpdated = info.title !== undefined;
|
|
||||||
if (tabLocationHasBeenUpdated || tabTitleHasBeenUpdated) {
|
|
||||||
const uri = await findTalerUriInTab(tabId);
|
|
||||||
if (!uri) return;
|
|
||||||
logger.info(`Found a Taler URI in the tab ${tabId}`);
|
|
||||||
callback(tabId, uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevHeaderListener = currentHeaderListener;
|
|
||||||
const prevTabListener = currentTabListener;
|
|
||||||
|
|
||||||
getPermissionsApi()
|
|
||||||
.containsHostPermissions()
|
|
||||||
.then((result) => {
|
|
||||||
//if there is a handler already, remove it
|
|
||||||
if (
|
|
||||||
prevHeaderListener &&
|
|
||||||
chrome?.webRequest?.onHeadersReceived?.hasListener(prevHeaderListener)
|
|
||||||
) {
|
|
||||||
chrome.webRequest.onHeadersReceived.removeListener(prevHeaderListener);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
prevTabListener &&
|
|
||||||
chrome?.tabs?.onUpdated?.hasListener(prevTabListener)
|
|
||||||
) {
|
|
||||||
chrome.tabs.onUpdated.removeListener(prevTabListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if the result was positive, add the headerListener
|
|
||||||
if (result) {
|
|
||||||
const headersEvent:
|
|
||||||
| chrome.webRequest.WebResponseHeadersEvent
|
|
||||||
| undefined = chrome?.webRequest?.onHeadersReceived;
|
|
||||||
if (headersEvent) {
|
|
||||||
headersEvent.addListener(headerListener, { urls: ["<all_urls>"] }, [
|
|
||||||
"responseHeaders",
|
|
||||||
]);
|
|
||||||
currentHeaderListener = headerListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabsEvent: chrome.tabs.TabUpdatedEvent | undefined =
|
|
||||||
chrome?.tabs?.onUpdated;
|
|
||||||
if (tabsEvent) {
|
|
||||||
tabsEvent.addListener(tabListener);
|
|
||||||
currentTabListener = tabListener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//notify the browser about this change, this operation is expensive
|
|
||||||
chrome?.webRequest?.handlerBehaviorChanged(() => {
|
|
||||||
if (chrome.runtime.lastError) {
|
|
||||||
logger.error(JSON.stringify(chrome.runtime.lastError));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const alertIcons = {
|
const alertIcons = {
|
||||||
"16": "/static/img/taler-alert-16.png",
|
"16": "/static/img/taler-alert-16.png",
|
||||||
"19": "/static/img/taler-alert-19.png",
|
"19": "/static/img/taler-alert-19.png",
|
||||||
@ -750,7 +667,7 @@ function registerOnInstalled(callback: () => void): void {
|
|||||||
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
|
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
registerIconChangeOnTalerContent();
|
await registerIconChangeOnTalerContent();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,18 +23,17 @@ import {
|
|||||||
MessageFromBackend,
|
MessageFromBackend,
|
||||||
MessageFromFrontend,
|
MessageFromFrontend,
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
|
defaultSettings,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
|
|
||||||
const frames = ["popup", "wallet"];
|
const frames = ["popup", "wallet"];
|
||||||
|
|
||||||
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
||||||
isFirefox: () => false,
|
isFirefox: () => false,
|
||||||
|
getSettingsFromStorage: () => Promise.resolve(defaultSettings),
|
||||||
keepAlive: (cb: VoidFunction) => cb(),
|
keepAlive: (cb: VoidFunction) => cb(),
|
||||||
findTalerUriInActiveTab: async () => undefined,
|
findTalerUriInActiveTab: async () => undefined,
|
||||||
findTalerUriInClipboard: async () => undefined,
|
findTalerUriInClipboard: async () => undefined,
|
||||||
containsTalerHeaderListener: () => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
getPermissionsApi: () => ({
|
getPermissionsApi: () => ({
|
||||||
addPermissionsListener: () => undefined,
|
addPermissionsListener: () => undefined,
|
||||||
containsHostPermissions: async () => true,
|
containsHostPermissions: async () => true,
|
||||||
@ -47,8 +46,9 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
|||||||
getWalletWebExVersion: () => ({
|
getWalletWebExVersion: () => ({
|
||||||
version: "none",
|
version: "none",
|
||||||
}),
|
}),
|
||||||
notifyWhenAppIsReady: (fn: () => void) => {
|
notifyWhenAppIsReady: () => {
|
||||||
let total = frames.length;
|
let total = frames.length;
|
||||||
|
return new Promise((fn) => {
|
||||||
function waitAndNotify(): void {
|
function waitAndNotify(): void {
|
||||||
total--;
|
total--;
|
||||||
if (total < 1) {
|
if (total < 1) {
|
||||||
@ -63,6 +63,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
|||||||
theFrame.addEventListener("load", waitAndNotify);
|
theFrame.addEventListener("load", waitAndNotify);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
openWalletPage: (page: string) => {
|
openWalletPage: (page: string) => {
|
||||||
@ -80,9 +81,8 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
registerAllIncomingConnections: () => undefined,
|
registerAllIncomingConnections: () => undefined,
|
||||||
registerOnInstalled: (fn: () => void) => undefined,
|
registerOnInstalled: () => Promise.resolve(),
|
||||||
registerReloadOnNewVersion: () => undefined,
|
registerReloadOnNewVersion: () => undefined,
|
||||||
registerTalerHeaderListener: () => undefined,
|
|
||||||
|
|
||||||
useServiceWorkerAsBackgroundProcess: () => false,
|
useServiceWorkerAsBackgroundProcess: () => false,
|
||||||
|
|
||||||
|
@ -19,11 +19,10 @@ import {
|
|||||||
CrossBrowserPermissionsApi,
|
CrossBrowserPermissionsApi,
|
||||||
ForegroundPlatformAPI,
|
ForegroundPlatformAPI,
|
||||||
Permissions,
|
Permissions,
|
||||||
|
Settings,
|
||||||
|
defaultSettings,
|
||||||
} from "./api.js";
|
} from "./api.js";
|
||||||
import chromePlatform, {
|
import chromePlatform, {
|
||||||
containsHostPermissions as chromeHostContains,
|
|
||||||
removeHostPermissions as chromeHostRemove,
|
|
||||||
requestHostPermissions as chromeHostRequest,
|
|
||||||
containsClipboardPermissions as chromeClipContains,
|
containsClipboardPermissions as chromeClipContains,
|
||||||
removeClipboardPermissions as chromeClipRemove,
|
removeClipboardPermissions as chromeClipRemove,
|
||||||
requestClipboardPermissions as chromeClipRequest,
|
requestClipboardPermissions as chromeClipRequest,
|
||||||
@ -32,6 +31,7 @@ import chromePlatform, {
|
|||||||
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
|
||||||
...chromePlatform,
|
...chromePlatform,
|
||||||
isFirefox,
|
isFirefox,
|
||||||
|
getSettingsFromStorage,
|
||||||
getPermissionsApi,
|
getPermissionsApi,
|
||||||
notifyWhenAppIsReady,
|
notifyWhenAppIsReady,
|
||||||
redirectTabToWalletPage,
|
redirectTabToWalletPage,
|
||||||
@ -51,25 +51,43 @@ function addPermissionsListener(callback: (p: Permissions) => void): void {
|
|||||||
function getPermissionsApi(): CrossBrowserPermissionsApi {
|
function getPermissionsApi(): CrossBrowserPermissionsApi {
|
||||||
return {
|
return {
|
||||||
addPermissionsListener,
|
addPermissionsListener,
|
||||||
containsHostPermissions: chromeHostContains,
|
// containsHostPermissions: chromeHostContains,
|
||||||
requestHostPermissions: chromeHostRequest,
|
// requestHostPermissions: chromeHostRequest,
|
||||||
removeHostPermissions: chromeHostRemove,
|
// removeHostPermissions: chromeHostRemove,
|
||||||
containsClipboardPermissions: chromeClipContains,
|
containsClipboardPermissions: chromeClipContains,
|
||||||
removeClipboardPermissions: chromeClipRemove,
|
removeClipboardPermissions: chromeClipRemove,
|
||||||
requestClipboardPermissions: chromeClipRequest,
|
requestClipboardPermissions: chromeClipRequest,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getSettingsFromStorage(): Promise<Settings> {
|
||||||
|
//@ts-ignore
|
||||||
|
const data = await browser.storage.local.get("wallet-settings");
|
||||||
|
if (!data) return defaultSettings;
|
||||||
|
const settings = data["wallet-settings"];
|
||||||
|
if (!settings) return defaultSettings;
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(settings);
|
||||||
|
return parsed;
|
||||||
|
} catch (e) {
|
||||||
|
return defaultSettings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param callback function to be called
|
* @param callback function to be called
|
||||||
*/
|
*/
|
||||||
function notifyWhenAppIsReady(callback: () => void): void {
|
function notifyWhenAppIsReady(): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) {
|
if (chrome.runtime && chrome.runtime.getManifest().manifest_version === 3) {
|
||||||
callback();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener("load", callback);
|
window.addEventListener("load", () => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function redirectTabToWalletPage(tabId: number, page: string): void {
|
function redirectTabToWalletPage(tabId: number, page: string): void {
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { CoreApiResponse } from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will modify all the pages that the user load when navigating with Web Extension enabled
|
* This will modify all the pages that the user load when navigating with Web Extension enabled
|
||||||
*
|
*
|
||||||
@ -62,7 +64,7 @@ const logger = {
|
|||||||
console.error(`${new Date().toISOString()} TALER`, ...msg),
|
console.error(`${new Date().toISOString()} TALER`, ...msg),
|
||||||
};
|
};
|
||||||
|
|
||||||
function start() {
|
async function start() {
|
||||||
if (shouldNotInject) {
|
if (shouldNotInject) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -73,8 +75,15 @@ function start() {
|
|||||||
}
|
}
|
||||||
createBridgeWithExtension();
|
createBridgeWithExtension();
|
||||||
logger.debug("bridged created");
|
logger.debug("bridged created");
|
||||||
|
|
||||||
|
const shouldInject = await callBackground("isInjectionEnabled", undefined);
|
||||||
|
|
||||||
|
if (shouldInject) {
|
||||||
injectTalerSupportScript(debugEnabled);
|
injectTalerSupportScript(debugEnabled);
|
||||||
logger.debug("done");
|
logger.debug("injection completed");
|
||||||
|
} else {
|
||||||
|
logger.debug("injection is not enabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,4 +141,65 @@ function createBridgeWithExtension() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExtensionOperations {
|
||||||
|
isInjectionEnabled: {
|
||||||
|
request: void;
|
||||||
|
response: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageFromExtension<Op extends keyof ExtensionOperations> = {
|
||||||
|
channel: "extension";
|
||||||
|
operation: Op;
|
||||||
|
payload: ExtensionOperations[Op]["request"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessageResponse = CoreApiResponse;
|
||||||
|
|
||||||
|
async function callBackground<Op extends keyof ExtensionOperations>(
|
||||||
|
operation: Op,
|
||||||
|
payload: ExtensionOperations[Op]["request"],
|
||||||
|
): Promise<ExtensionOperations[Op]["response"]> {
|
||||||
|
const message: MessageFromExtension<Op> = {
|
||||||
|
channel: "extension",
|
||||||
|
operation,
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await sendMessageToBackground(message);
|
||||||
|
if (response.type === "error") {
|
||||||
|
throw new Error(`Background operation "${operation}" failed`);
|
||||||
|
}
|
||||||
|
return response.result as any;
|
||||||
|
}
|
||||||
|
let nextMessageIndex = 0;
|
||||||
|
async function sendMessageToBackground<Op extends keyof ExtensionOperations>(
|
||||||
|
message: MessageFromExtension<Op>,
|
||||||
|
): Promise<MessageResponse> {
|
||||||
|
const messageWithId = { ...message, id: `id_${nextMessageIndex++ % 1000}` };
|
||||||
|
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
// logger.trace("send operation to the wallet background", message);
|
||||||
|
let timedout = false;
|
||||||
|
const timerId = setTimeout(() => {
|
||||||
|
timedout = true;
|
||||||
|
throw new Error("timeout");
|
||||||
|
// throw TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {});
|
||||||
|
}, 5 * 1000); //five seconds
|
||||||
|
chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => {
|
||||||
|
if (timedout) {
|
||||||
|
return false; //already rejected
|
||||||
|
}
|
||||||
|
clearTimeout(timerId);
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
reject(chrome.runtime.lastError.message);
|
||||||
|
} else {
|
||||||
|
resolve(backgroundResponse);
|
||||||
|
}
|
||||||
|
// return true to keep the channel open
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
@ -92,7 +92,7 @@ function buildApi(config: Readonly<Info>): API {
|
|||||||
}
|
}
|
||||||
const targetAttr = ev.currentTarget.attributes.getNamedItem("target");
|
const targetAttr = ev.currentTarget.attributes.getNamedItem("target");
|
||||||
const windowTarget =
|
const windowTarget =
|
||||||
targetAttr && targetAttr.value ? targetAttr.value : "taler-wallet";
|
targetAttr && targetAttr.value ? targetAttr.value : "_self";
|
||||||
const page = convertURIToWebExtensionPath(hrefAttr.value);
|
const page = convertURIToWebExtensionPath(hrefAttr.value);
|
||||||
if (!page) {
|
if (!page) {
|
||||||
logger.debug(`onclick: could not convert "${hrefAttr.value}" into path`);
|
logger.debug(`onclick: could not convert "${hrefAttr.value}" into path`);
|
||||||
|
@ -271,7 +271,7 @@ export function SettingsView({
|
|||||||
<i18n.Translate>Navigator</i18n.Translate>
|
<i18n.Translate>Navigator</i18n.Translate>
|
||||||
</SubTitle>
|
</SubTitle>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={i18n.str`Automatically inject Taler API in all pages`}
|
label={i18n.str`Inject Taler support in all pages`}
|
||||||
name="inject"
|
name="inject"
|
||||||
description={
|
description={
|
||||||
<i18n.Translate>
|
<i18n.Translate>
|
||||||
|
@ -100,7 +100,7 @@ export function View({
|
|||||||
<i18n.Translate>Navigator</i18n.Translate>
|
<i18n.Translate>Navigator</i18n.Translate>
|
||||||
</SubTitle>
|
</SubTitle>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={i18n.str`Automatically inject Taler API in all pages`}
|
label={i18n.str`Inject Taler support in all pages`}
|
||||||
name="inject"
|
name="inject"
|
||||||
description={
|
description={
|
||||||
<i18n.Translate>
|
<i18n.Translate>
|
||||||
|
@ -68,18 +68,10 @@ export interface BackgroundOperations {
|
|||||||
request: void;
|
request: void;
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
containsHeaderListener: {
|
|
||||||
request: void;
|
|
||||||
response: ExtendedPermissionsResponse;
|
|
||||||
};
|
|
||||||
getDiagnostics: {
|
getDiagnostics: {
|
||||||
request: void;
|
request: void;
|
||||||
response: WalletDiagnostics;
|
response: WalletDiagnostics;
|
||||||
};
|
};
|
||||||
toggleHeaderListener: {
|
|
||||||
request: boolean;
|
|
||||||
response: ExtendedPermissionsResponse;
|
|
||||||
};
|
|
||||||
runGarbageCollector: {
|
runGarbageCollector: {
|
||||||
request: void;
|
request: void;
|
||||||
response: void;
|
response: void;
|
||||||
|
@ -24,42 +24,39 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
classifyTalerUri,
|
|
||||||
Logger,
|
|
||||||
LogLevel,
|
LogLevel,
|
||||||
|
Logger,
|
||||||
|
TalerErrorCode,
|
||||||
|
WalletDiagnostics,
|
||||||
|
getErrorDetailFromException,
|
||||||
|
makeErrorDetail,
|
||||||
setGlobalLogLevelFromString,
|
setGlobalLogLevelFromString,
|
||||||
setLogLevelFromString,
|
setLogLevelFromString,
|
||||||
TalerErrorCode,
|
|
||||||
TalerUriType,
|
|
||||||
WalletDiagnostics,
|
|
||||||
makeErrorDetail,
|
|
||||||
getErrorDetailFromException,
|
|
||||||
parseTalerUri,
|
|
||||||
TalerUriAction,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
DbAccess,
|
DbAccess,
|
||||||
deleteTalerDatabase,
|
|
||||||
exportDb,
|
|
||||||
importDb,
|
|
||||||
OpenedPromise,
|
OpenedPromise,
|
||||||
openPromise,
|
|
||||||
openTalerDatabase,
|
|
||||||
SetTimeoutTimerAPI,
|
SetTimeoutTimerAPI,
|
||||||
Wallet,
|
Wallet,
|
||||||
WalletOperations,
|
WalletOperations,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
|
deleteTalerDatabase,
|
||||||
|
exportDb,
|
||||||
|
importDb,
|
||||||
|
openPromise,
|
||||||
|
openTalerDatabase,
|
||||||
} from "@gnu-taler/taler-wallet-core";
|
} from "@gnu-taler/taler-wallet-core";
|
||||||
import { BrowserHttpLib } from "./browserHttpLib.js";
|
import { BrowserHttpLib } from "./browserHttpLib.js";
|
||||||
import { platform } from "./platform/background.js";
|
|
||||||
import {
|
import {
|
||||||
MessageFromBackend,
|
MessageFromBackend,
|
||||||
MessageFromFrontend,
|
MessageFromFrontend,
|
||||||
MessageResponse,
|
MessageResponse,
|
||||||
} from "./platform/api.js";
|
} from "./platform/api.js";
|
||||||
|
import { platform } from "./platform/background.js";
|
||||||
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory.js";
|
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory.js";
|
||||||
import { ServiceWorkerHttpLib } from "./serviceWorkerHttpLib.js";
|
import { ServiceWorkerHttpLib } from "./serviceWorkerHttpLib.js";
|
||||||
import { BackgroundOperations, ExtendedPermissionsResponse } from "./wxApi.js";
|
import { ExtensionOperations } from "./taler-wallet-interaction-loader.js";
|
||||||
|
import { BackgroundOperations } from "./wxApi.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently active wallet instance. Might be unloaded and
|
* Currently active wallet instance. Might be unloaded and
|
||||||
@ -123,10 +120,11 @@ type BackendHandlerType = {
|
|||||||
) => Promise<BackgroundOperations[Op]["response"]>;
|
) => Promise<BackgroundOperations[Op]["response"]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function containsHeaderListener(): Promise<ExtendedPermissionsResponse> {
|
type ExtensionHandlerType = {
|
||||||
const result = await platform.containsTalerHeaderListener();
|
[Op in keyof ExtensionOperations]: (
|
||||||
return { newValue: result };
|
req: ExtensionOperations[Op]["request"],
|
||||||
}
|
) => Promise<ExtensionOperations[Op]["response"]>;
|
||||||
|
};
|
||||||
|
|
||||||
async function resetDb(): Promise<void> {
|
async function resetDb(): Promise<void> {
|
||||||
await deleteTalerDatabase(indexedDB as any);
|
await deleteTalerDatabase(indexedDB as any);
|
||||||
@ -153,20 +151,6 @@ async function runGarbageCollector(): Promise<void> {
|
|||||||
logger.info("imported");
|
logger.info("imported");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleHeaderListener(
|
|
||||||
newVal: boolean,
|
|
||||||
): Promise<ExtendedPermissionsResponse> {
|
|
||||||
logger.trace("new extended permissions value", newVal);
|
|
||||||
if (newVal) {
|
|
||||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
|
||||||
return { newValue: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
const rem = await platform.getPermissionsApi().removeHostPermissions();
|
|
||||||
logger.trace("permissions removed:", rem);
|
|
||||||
return { newValue: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
function freeze(time: number): Promise<void> {
|
function freeze(time: number): Promise<void> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
setTimeout(res, time);
|
setTimeout(res, time);
|
||||||
@ -177,14 +161,21 @@ async function sum(ns: Array<number>): Promise<number> {
|
|||||||
return ns.reduce((prev, cur) => prev + cur, 0);
|
return ns.reduce((prev, cur) => prev + cur, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extensionHandlers: ExtensionHandlerType = {
|
||||||
|
isInjectionEnabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function isInjectionEnabled(): Promise<boolean> {
|
||||||
|
const settings = await platform.getSettingsFromStorage();
|
||||||
|
return settings.injectTalerSupport === true;
|
||||||
|
}
|
||||||
|
|
||||||
const backendHandlers: BackendHandlerType = {
|
const backendHandlers: BackendHandlerType = {
|
||||||
freeze,
|
freeze,
|
||||||
sum,
|
sum,
|
||||||
containsHeaderListener,
|
|
||||||
getDiagnostics,
|
getDiagnostics,
|
||||||
resetDb,
|
resetDb,
|
||||||
runGarbageCollector,
|
runGarbageCollector,
|
||||||
toggleHeaderListener,
|
|
||||||
setLoggingLevel,
|
setLoggingLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,10 +194,11 @@ async function setLoggingLevel({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dispatch<Op extends WalletOperations | BackgroundOperations>(
|
async function dispatch<
|
||||||
req: MessageFromFrontend<Op> & { id: string },
|
Op extends WalletOperations | BackgroundOperations | ExtensionOperations,
|
||||||
): Promise<MessageResponse> {
|
>(req: MessageFromFrontend<Op> & { id: string }): Promise<MessageResponse> {
|
||||||
if (req.channel === "background") {
|
switch (req.channel) {
|
||||||
|
case "background": {
|
||||||
const handler = backendHandlers[req.operation] as (req: any) => any;
|
const handler = backendHandlers[req.operation] as (req: any) => any;
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
return {
|
return {
|
||||||
@ -235,8 +227,36 @@ async function dispatch<Op extends WalletOperations | BackgroundOperations>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "extension": {
|
||||||
if (req.channel === "wallet") {
|
const handler = extensionHandlers[req.operation] as (req: any) => any;
|
||||||
|
if (!handler) {
|
||||||
|
return {
|
||||||
|
type: "error",
|
||||||
|
id: req.id,
|
||||||
|
operation: String(req.operation),
|
||||||
|
error: getErrorDetailFromException(
|
||||||
|
Error(`unknown extension operation`),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const result = await handler(req.payload);
|
||||||
|
return {
|
||||||
|
type: "response",
|
||||||
|
id: req.id,
|
||||||
|
operation: String(req.operation),
|
||||||
|
result,
|
||||||
|
};
|
||||||
|
} catch (er) {
|
||||||
|
return {
|
||||||
|
type: "error",
|
||||||
|
id: req.id,
|
||||||
|
error: getErrorDetailFromException(er),
|
||||||
|
operation: String(req.operation),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "wallet": {
|
||||||
const w = currentWallet;
|
const w = currentWallet;
|
||||||
if (!w) {
|
if (!w) {
|
||||||
return {
|
return {
|
||||||
@ -253,6 +273,7 @@ async function dispatch<Op extends WalletOperations | BackgroundOperations>(
|
|||||||
|
|
||||||
return await w.handleCoreApiRequest(req.operation, req.id, req.payload);
|
return await w.handleCoreApiRequest(req.operation, req.id, req.payload);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const anyReq = req as any;
|
const anyReq = req as any;
|
||||||
return {
|
return {
|
||||||
@ -261,7 +282,7 @@ async function dispatch<Op extends WalletOperations | BackgroundOperations>(
|
|||||||
operation: String(anyReq.operation),
|
operation: String(anyReq.operation),
|
||||||
error: getErrorDetailFromException(
|
error: getErrorDetailFromException(
|
||||||
Error(
|
Error(
|
||||||
`unknown channel ${anyReq.channel}, should be "background" or "wallet"`,
|
`unknown channel ${anyReq.channel}, should be "background", "extension" or "wallet"`,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -330,23 +351,6 @@ async function reinitWallet(): Promise<void> {
|
|||||||
return walletInit.resolve();
|
return walletInit.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTalerUriAndRedirect(tabId: number, maybeTalerUri: string): void {
|
|
||||||
const talerUri = maybeTalerUri.startsWith("ext+")
|
|
||||||
? maybeTalerUri.substring(4)
|
|
||||||
: maybeTalerUri;
|
|
||||||
const uri = parseTalerUri(talerUri);
|
|
||||||
if (!uri) {
|
|
||||||
logger.warn(
|
|
||||||
`Response with HTTP 402 the Taler header but could not classify ${talerUri}`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return platform.redirectTabToWalletPage(
|
|
||||||
tabId,
|
|
||||||
`/taler-uri/${encodeURIComponent(talerUri)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function to run for the WebExtension backend.
|
* Main function to run for the WebExtension backend.
|
||||||
*
|
*
|
||||||
@ -370,30 +374,10 @@ export async function wxMain(): Promise<void> {
|
|||||||
platform.registerAllIncomingConnections();
|
platform.registerAllIncomingConnections();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
platform.registerOnInstalled(() => {
|
await platform.registerOnInstalled(() => {
|
||||||
platform.openWalletPage("/welcome");
|
platform.openWalletPage("/welcome");
|
||||||
|
|
||||||
//
|
|
||||||
try {
|
|
||||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("could not register header listener", e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// On platforms that support it, also listen to external
|
|
||||||
// modification of permissions.
|
|
||||||
platform.getPermissionsApi().addPermissionsListener((perm, lastError) => {
|
|
||||||
if (lastError) {
|
|
||||||
logger.error(
|
|
||||||
`there was a problem trying to get permission ${perm}`,
|
|
||||||
lastError,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
platform.registerTalerHeaderListener(parseTalerUriAndRedirect);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user