#7120 manifest v3: first iteration working
new permission needed: scripting chrome.browserAction -> chrome.action webRequestBlocking is not possible anymore chrome.extension.getUrl -> chrome.runtime.getUrl new serviceWorkerHttpLib: using fetch new serviceWorkerCryptoWorkerFactory: using syncCryptoImpl few other minor changes still missing some other changes like migrating setTimeout to chrome.alarms api
This commit is contained in:
parent
f8ae2671c1
commit
bc817a638d
@ -1,41 +1,28 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
|
||||
"name": "GNU Taler Wallet (git)",
|
||||
"description": "Privacy preserving and transparent payments",
|
||||
"author": "GNU Taler Developers",
|
||||
"version": "0.8.1.15",
|
||||
"version_name": "0.8.1-dev.15",
|
||||
|
||||
"minimum_chrome_version": "88",
|
||||
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "wallet@taler.net",
|
||||
"strict_min_version": "57.0"
|
||||
}
|
||||
},
|
||||
|
||||
"icons": {
|
||||
"32": "static/img/icon.png",
|
||||
"128": "static/img/logo.png"
|
||||
},
|
||||
|
||||
"permissions": [
|
||||
"unlimitedStorage",
|
||||
"activeTab"
|
||||
"activeTab",
|
||||
"scripting"
|
||||
],
|
||||
|
||||
"optional_permissions": [
|
||||
"webRequest",
|
||||
"webRequestBlocking"
|
||||
],
|
||||
|
||||
"host_permissions":[
|
||||
"host_permissions": [
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
|
||||
"action": {
|
||||
"default_icon": {
|
||||
"32": "static/img/icon.png"
|
||||
@ -43,9 +30,7 @@
|
||||
"default_title": "Taler",
|
||||
"default_popup": "static/popup.html"
|
||||
},
|
||||
|
||||
"background": {
|
||||
"page": "static/background.html",
|
||||
"persistent": true
|
||||
"service_worker": "dist/background.js"
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@
|
||||
"@storybook/preact": "6.4.9",
|
||||
"@testing-library/preact": "^2.0.1",
|
||||
"@testing-library/preact-hooks": "^1.1.0",
|
||||
"@types/chrome": "^0.0.174",
|
||||
"@types/chrome": "0.0.176",
|
||||
"@types/history": "^4.7.8",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^17.0.8",
|
||||
|
@ -1,6 +1,36 @@
|
||||
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: `
|
||||
|
@ -25,6 +25,12 @@
|
||||
*/
|
||||
import { wxMain } from "./wxBackend";
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const loadedFromWebpage = typeof window !== "undefined"
|
||||
|
||||
if (chrome.runtime.getManifest().manifest_version === 3) {
|
||||
wxMain();
|
||||
});
|
||||
} else {
|
||||
window.addEventListener("load", () => {
|
||||
wxMain();
|
||||
});
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ export class ChromeBadge {
|
||||
this.canvas.width,
|
||||
this.canvas.height,
|
||||
);
|
||||
chrome.browserAction.setIcon({ imageData });
|
||||
chrome.action.setIcon({ imageData });
|
||||
} catch (e) {
|
||||
// Might fail if browser has over-eager canvas fingerprinting countermeasures.
|
||||
// There's nothing we can do then ...
|
||||
|
@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
export const extendedPermissions = {
|
||||
permissions: ["webRequest", "webRequestBlocking"],
|
||||
permissions: ["webRequest"],
|
||||
origins: ["http://*/*", "https://*/*"],
|
||||
};
|
||||
|
@ -194,7 +194,7 @@ export function openExtensionPage(page: string) {
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.tabs.create({
|
||||
// eslint-disable-next-line no-undef
|
||||
url: chrome.extension.getURL(page),
|
||||
url: chrome.runtime.getURL(page),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -60,9 +60,9 @@ export function SettingsView({
|
||||
style={{ color: "darkgreen", textDecoration: "none" }}
|
||||
href={
|
||||
// eslint-disable-next-line no-undef
|
||||
chrome.extension
|
||||
chrome.runtime
|
||||
? // eslint-disable-next-line no-undef
|
||||
chrome.extension.getURL(`/static/wallet.html#/settings`)
|
||||
chrome.runtime.getURL(`/static/wallet.html#/settings`)
|
||||
: "#"
|
||||
}
|
||||
>
|
||||
|
@ -173,7 +173,7 @@ function goToWalletPage(page: Pages | string): null {
|
||||
chrome.tabs.create({
|
||||
active: true,
|
||||
// eslint-disable-next-line no-undef
|
||||
url: chrome.extension.getURL(`/static/wallet.html#${page}`),
|
||||
url: chrome.runtime.getURL(`/static/wallet.html#${page}`),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ export function PageLink(props: {
|
||||
typeof chrome === "undefined"
|
||||
? undefined
|
||||
: // eslint-disable-next-line no-undef
|
||||
chrome.extension?.getURL(`/static/wallet.html#/${props.pageName}`);
|
||||
chrome.runtime?.getURL(`/static/wallet.html#/${props.pageName}`);
|
||||
return (
|
||||
<a class="actionLink" href={url} target="_blank" rel="noopener noreferrer">
|
||||
{props.children}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2016 GNUnet e.V.
|
||||
|
||||
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/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* API to access the Taler crypto worker thread.
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
import {
|
||||
CryptoWorker,
|
||||
CryptoWorkerFactory,
|
||||
SynchronousCryptoWorker,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
|
||||
export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
|
||||
startWorker(): CryptoWorker {
|
||||
return new SynchronousCryptoWorker();
|
||||
}
|
||||
|
||||
getConcurrency(): number {
|
||||
return 1;
|
||||
}
|
||||
}
|
146
packages/taler-wallet-webextension/src/serviceWorkerHttpLib.ts
Normal file
146
packages/taler-wallet-webextension/src/serviceWorkerHttpLib.ts
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2020 Taler Systems S.A.
|
||||
|
||||
GNU 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.
|
||||
|
||||
GNU 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
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { Logger, TalerErrorCode } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
Headers, HttpRequestLibrary,
|
||||
HttpRequestOptions,
|
||||
HttpResponse,
|
||||
OperationFailedError
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
|
||||
const logger = new Logger("browserHttpLib");
|
||||
|
||||
/**
|
||||
* An implementation of the [[HttpRequestLibrary]] using the
|
||||
* browser's XMLHttpRequest.
|
||||
*/
|
||||
export class ServiceWorkerHttpLib implements HttpRequestLibrary {
|
||||
async fetch(requestUrl: string, options?: HttpRequestOptions): Promise<HttpResponse> {
|
||||
const requestMethod = options?.method ?? "GET";
|
||||
const requestBody = options?.body;
|
||||
const requestHeader = options?.headers;
|
||||
|
||||
const response = await fetch(requestUrl, {
|
||||
headers: requestHeader,
|
||||
body: requestBody,
|
||||
method: requestMethod,
|
||||
// timeout: options?.timeout
|
||||
})
|
||||
|
||||
const headerMap = new Headers();
|
||||
response.headers.forEach(addLineToMap(headerMap));
|
||||
|
||||
return {
|
||||
headers: headerMap,
|
||||
status: response.status,
|
||||
requestMethod,
|
||||
requestUrl,
|
||||
json: makeJsonHandler(response, requestUrl),
|
||||
text: makeTextHandler(response, requestUrl),
|
||||
bytes: async () => (await response.blob()).arrayBuffer(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "GET",
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
postJson(
|
||||
url: string,
|
||||
body: any,
|
||||
opt?: HttpRequestOptions,
|
||||
): Promise<HttpResponse> {
|
||||
return this.fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
...opt,
|
||||
});
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
function makeTextHandler(response: Response, requestUrl: string) {
|
||||
return async function getJsonFromResponse(): Promise<any> {
|
||||
let respText;
|
||||
try {
|
||||
respText = await response.text()
|
||||
} catch (e) {
|
||||
throw OperationFailedError.fromCode(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
"Invalid JSON from HTTP response",
|
||||
{
|
||||
requestUrl,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
);
|
||||
}
|
||||
return respText
|
||||
}
|
||||
}
|
||||
|
||||
function makeJsonHandler(response: Response, requestUrl: string) {
|
||||
return async function getJsonFromResponse(): Promise<any> {
|
||||
let responseJson;
|
||||
try {
|
||||
responseJson = await response.json()
|
||||
} catch (e) {
|
||||
throw OperationFailedError.fromCode(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
"Invalid JSON from HTTP response",
|
||||
{
|
||||
requestUrl,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (responseJson === null || typeof responseJson !== "object") {
|
||||
throw OperationFailedError.fromCode(
|
||||
TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
|
||||
"Invalid JSON from HTTP response",
|
||||
{
|
||||
requestUrl,
|
||||
httpStatusCode: response.status,
|
||||
},
|
||||
);
|
||||
}
|
||||
return responseJson
|
||||
}
|
||||
}
|
||||
|
||||
function addLineToMap(map: { set(k: string, v: string): void }) {
|
||||
return (line: string) => {
|
||||
const parts = line.split(": ");
|
||||
const headerName = parts.shift();
|
||||
if (!headerName) {
|
||||
logger.warn("skipping invalid header");
|
||||
return;
|
||||
}
|
||||
const value = parts.join(": ");
|
||||
map.set(headerName, value);
|
||||
}
|
||||
}
|
@ -225,7 +225,7 @@ function makeExtensionUrlWithParams(
|
||||
params?: { [name: string]: string | undefined },
|
||||
): string {
|
||||
// eslint-disable-next-line no-undef
|
||||
const innerUrl = new URL(chrome.extension.getURL("/" + url));
|
||||
const innerUrl = new URL(chrome.runtime.getURL("/" + url));
|
||||
if (params) {
|
||||
const hParams = Object.keys(params)
|
||||
.map((k) => `${k}=${params[k]}`)
|
||||
|
@ -23,18 +23,6 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { isFirefox, getPermissionsApi } from "./compat";
|
||||
import { extendedPermissions } from "./permissions";
|
||||
import {
|
||||
OpenedPromise,
|
||||
openPromise,
|
||||
openTalerDatabase,
|
||||
makeErrorDetails,
|
||||
deleteTalerDatabase,
|
||||
DbAccess,
|
||||
WalletStoresV1,
|
||||
Wallet,
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import {
|
||||
classifyTalerUri,
|
||||
CoreApiResponse,
|
||||
@ -42,10 +30,19 @@ import {
|
||||
NotificationType,
|
||||
TalerErrorCode,
|
||||
TalerUriType,
|
||||
WalletDiagnostics,
|
||||
WalletDiagnostics
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { BrowserHttpLib } from "./browserHttpLib";
|
||||
import {
|
||||
DbAccess, deleteTalerDatabase, makeErrorDetails, OpenedPromise,
|
||||
openPromise,
|
||||
openTalerDatabase, Wallet, WalletStoresV1
|
||||
} from "@gnu-taler/taler-wallet-core";
|
||||
import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
||||
import { BrowserHttpLib } from "./browserHttpLib";
|
||||
import { getPermissionsApi, isFirefox } from "./compat";
|
||||
import { extendedPermissions } from "./permissions";
|
||||
import { SynchronousCryptoWorkerFactory } from "./serviceWorkerCryptoWorkerFactory.js";
|
||||
import { ServiceWorkerHttpLib } from "./serviceWorkerHttpLib";
|
||||
|
||||
/**
|
||||
* Currently active wallet instance. Might be unloaded and
|
||||
@ -188,10 +185,10 @@ function getTab(tabId: number): Promise<chrome.tabs.Tab> {
|
||||
});
|
||||
}
|
||||
|
||||
function setBadgeText(options: chrome.browserAction.BadgeTextDetails): void {
|
||||
function setBadgeText(options: chrome.action.BadgeTextDetails): void {
|
||||
// not supported by all browsers ...
|
||||
if (chrome && chrome.browserAction && chrome.browserAction.setBadgeText) {
|
||||
chrome.browserAction.setBadgeText(options);
|
||||
if (chrome && chrome.action && chrome.action.setBadgeText) {
|
||||
chrome.action.setBadgeText(options);
|
||||
} else {
|
||||
console.warn("can't set badge text, not supported", options);
|
||||
}
|
||||
@ -214,7 +211,7 @@ function makeSyncWalletRedirect(
|
||||
oldUrl: string,
|
||||
params?: { [name: string]: string | undefined },
|
||||
): Record<string, unknown> {
|
||||
const innerUrl = new URL(chrome.extension.getURL(url));
|
||||
const innerUrl = new URL(chrome.runtime.getURL(url));
|
||||
if (params) {
|
||||
const hParams = Object.keys(params)
|
||||
.map((k) => `${k}=${params[k]}`)
|
||||
@ -256,12 +253,22 @@ async function reinitWallet(): Promise<void> {
|
||||
walletInit.reject(e);
|
||||
return;
|
||||
}
|
||||
const http = new BrowserHttpLib();
|
||||
let httpLib;
|
||||
let cryptoWorker;
|
||||
|
||||
if (chrome.runtime.getManifest().manifest_version === 3) {
|
||||
httpLib = new ServiceWorkerHttpLib()
|
||||
cryptoWorker = new SynchronousCryptoWorkerFactory();
|
||||
} else {
|
||||
httpLib = new BrowserHttpLib()
|
||||
cryptoWorker = new BrowserCryptoWorkerFactory()
|
||||
}
|
||||
|
||||
console.log("setting wallet");
|
||||
const wallet = await Wallet.create(
|
||||
currentDatabase,
|
||||
http,
|
||||
new BrowserCryptoWorkerFactory(),
|
||||
httpLib,
|
||||
cryptoWorker,
|
||||
);
|
||||
try {
|
||||
await wallet.handleCoreApiRequest("initWallet", "native-init", {});
|
||||
@ -284,7 +291,9 @@ async function reinitWallet(): Promise<void> {
|
||||
console.log("error during wallet task loop", e);
|
||||
});
|
||||
// Useful for debugging in the background page.
|
||||
if (typeof window !== "undefined") {
|
||||
(window as any).talerWallet = wallet;
|
||||
}
|
||||
currentWallet = wallet;
|
||||
walletInit.resolve();
|
||||
}
|
||||
@ -295,8 +304,8 @@ try {
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
console.log("onInstalled with reason", details.reason);
|
||||
if (details.reason === "install") {
|
||||
const url = chrome.extension.getURL("/static/wallet.html#/welcome");
|
||||
chrome.tabs.create({ active: true, url: url });
|
||||
const url = chrome.runtime.getURL("/static/wallet.html#/welcome");
|
||||
chrome.tabs.create({ active: true, url });
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
@ -387,6 +396,10 @@ function headerListener(
|
||||
}
|
||||
|
||||
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(extendedPermissions, (result: boolean) => {
|
||||
@ -427,12 +440,14 @@ export async function wxMain(): Promise<void> {
|
||||
console.log("update available:", details);
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
reinitWallet();
|
||||
const afterWalletIsInitialized = reinitWallet();
|
||||
|
||||
// Handlers for messages coming directly from the content
|
||||
// script on the page
|
||||
chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
|
||||
afterWalletIsInitialized.then(() => {
|
||||
dispatch(req, sender, sendResponse);
|
||||
})
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -447,7 +462,9 @@ export async function wxMain(): Promise<void> {
|
||||
});
|
||||
|
||||
try {
|
||||
if (chrome.runtime.getManifest().manifest_version === 2) {
|
||||
setupHeaderListener();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ importers:
|
||||
'@storybook/preact': 6.4.9
|
||||
'@testing-library/preact': ^2.0.1
|
||||
'@testing-library/preact-hooks': ^1.1.0
|
||||
'@types/chrome': ^0.0.174
|
||||
'@types/chrome': 0.0.176
|
||||
'@types/history': ^4.7.8
|
||||
'@types/mocha': ^9.0.0
|
||||
'@types/node': ^17.0.8
|
||||
@ -393,7 +393,7 @@ importers:
|
||||
'@storybook/preact': 6.4.9_7ac135b2eab8a45315147b85be8cb430
|
||||
'@testing-library/preact': 2.0.1_preact@10.5.14
|
||||
'@testing-library/preact-hooks': 1.1.0_6273b572ba1ed58b8bbb2ee93959f9e4
|
||||
'@types/chrome': 0.0.174
|
||||
'@types/chrome': 0.0.176
|
||||
'@types/history': 4.7.9
|
||||
'@types/mocha': 9.0.0
|
||||
'@types/node': 17.0.8
|
||||
@ -9654,8 +9654,8 @@ packages:
|
||||
'@types/node': 17.0.8
|
||||
dev: true
|
||||
|
||||
/@types/chrome/0.0.174:
|
||||
resolution: {integrity: sha512-x5kjvNdwDtOnT+vbnksj69pDl0u9P/WH9LbQWJawLqGgkBRO3AN/xzTxTPgLpp3IqCbuwfp7bRCHqkkaZguzWw==}
|
||||
/@types/chrome/0.0.176:
|
||||
resolution: {integrity: sha512-LOveFOMIUhMJjvRzZv5whGBpncP/gdJ4hcxeAqg94wGi6CyKaCmLgFSofgItf85GuLTl/0BQ6J/Y1e8BqZWfEg==}
|
||||
dependencies:
|
||||
'@types/filesystem': 0.0.32
|
||||
'@types/har-format': 1.2.8
|
||||
|
Loading…
Reference in New Issue
Block a user