drastically reduce permissions for Web integration
The old web integration with more permissions is still available on an opt-in basis.
This commit is contained in:
parent
3f52d293be
commit
609397d95a
@ -475,3 +475,8 @@ export interface DepositInfo {
|
|||||||
denomPub: string;
|
denomPub: string;
|
||||||
denomSig: string;
|
denomSig: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface ExtendedPermissionsResponse {
|
||||||
|
newValue: boolean;
|
||||||
|
}
|
@ -164,6 +164,14 @@ export interface MessageMap {
|
|||||||
request: {};
|
request: {};
|
||||||
response: walletTypes.WalletDiagnostics;
|
response: walletTypes.WalletDiagnostics;
|
||||||
};
|
};
|
||||||
|
"set-extended-permissions": {
|
||||||
|
request: { value: boolean };
|
||||||
|
response: walletTypes.ExtendedPermissionsResponse;
|
||||||
|
};
|
||||||
|
"get-extended-permissions": {
|
||||||
|
request: { };
|
||||||
|
response: walletTypes.ExtendedPermissionsResponse;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +24,7 @@ import ReactDOM from "react-dom";
|
|||||||
import { createPopup } from "./pages/popup";
|
import { createPopup } from "./pages/popup";
|
||||||
import { createWithdrawPage } from "./pages/withdraw";
|
import { createWithdrawPage } from "./pages/withdraw";
|
||||||
import { createWelcomePage } from "./pages/welcome";
|
import { createWelcomePage } from "./pages/welcome";
|
||||||
|
import { createPayPage } from "./pages/pay";
|
||||||
|
|
||||||
function main(): void {
|
function main(): void {
|
||||||
try {
|
try {
|
||||||
@ -43,6 +44,9 @@ function main(): void {
|
|||||||
case "welcome.html":
|
case "welcome.html":
|
||||||
mainElement = createWelcomePage();
|
mainElement = createWelcomePage();
|
||||||
break;
|
break;
|
||||||
|
case "pay.html":
|
||||||
|
mainElement = createPayPage();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw Error(`page '${page}' not implemented`);
|
throw Error(`page '${page}' not implemented`);
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makePayPage(): JSX.Element {
|
export function createPayPage(): JSX.Element {
|
||||||
const url = new URL(document.location.href);
|
const url = new URL(document.location.href);
|
||||||
const talerPayUri = url.searchParams.get("talerPayUri");
|
const talerPayUri = url.searchParams.get("talerPayUri");
|
||||||
if (!talerPayUri) {
|
if (!talerPayUri) {
|
||||||
|
@ -34,11 +34,12 @@ import { WalletBalance, WalletBalanceEntry } from "../../types/walletTypes";
|
|||||||
import { abbrev, renderAmount, PageLink } from "../renderHtml";
|
import { abbrev, renderAmount, PageLink } from "../renderHtml";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
|
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment, useState, useEffect } from "react";
|
||||||
import { HistoryEvent } from "../../types/history";
|
import { HistoryEvent } from "../../types/history";
|
||||||
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Timestamp } from "../../util/time";
|
import { Timestamp } from "../../util/time";
|
||||||
|
import { classifyTalerUri, TalerUriType } from "../../util/taleruri";
|
||||||
|
|
||||||
// FIXME: move to newer react functions
|
// FIXME: move to newer react functions
|
||||||
/* eslint-disable react/no-deprecated */
|
/* eslint-disable react/no-deprecated */
|
||||||
@ -761,7 +762,113 @@ function openTab(page: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeExtensionUrlWithParams(
|
||||||
|
url: string,
|
||||||
|
params?: { [name: string]: string | undefined },
|
||||||
|
): string {
|
||||||
|
const innerUrl = new URL(chrome.extension.getURL("/" + url));
|
||||||
|
if (params) {
|
||||||
|
for (const key in params) {
|
||||||
|
const p = params[key];
|
||||||
|
if (p) {
|
||||||
|
innerUrl.searchParams.set(key, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return innerUrl.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionForTalerUri(talerUri: string): string | undefined {
|
||||||
|
const uriType = classifyTalerUri(talerUri);
|
||||||
|
switch (uriType) {
|
||||||
|
case TalerUriType.TalerWithdraw:
|
||||||
|
return makeExtensionUrlWithParams("withdraw.html", {
|
||||||
|
talerWithdrawUri: talerUri,
|
||||||
|
});
|
||||||
|
case TalerUriType.TalerPay:
|
||||||
|
return makeExtensionUrlWithParams("pay.html", {
|
||||||
|
talerPayUri: talerUri,
|
||||||
|
});
|
||||||
|
case TalerUriType.TalerTip:
|
||||||
|
return makeExtensionUrlWithParams("tip.html", {
|
||||||
|
talerTipUri: talerUri,
|
||||||
|
});
|
||||||
|
case TalerUriType.TalerRefund:
|
||||||
|
return makeExtensionUrlWithParams("refund.html", {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findTalerUriInActiveTab(): Promise<string | undefined> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
chrome.tabs.executeScript(
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
(() => {
|
||||||
|
let x = document.querySelector("a[href^='taler://'");
|
||||||
|
return x ? x.href.toString() : null;
|
||||||
|
})();
|
||||||
|
`,
|
||||||
|
allFrames: false,
|
||||||
|
},
|
||||||
|
(result) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error(chrome.runtime.lastError);
|
||||||
|
resolve(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("got result", result);
|
||||||
|
resolve(result[0]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function WalletPopup(): JSX.Element {
|
function WalletPopup(): JSX.Element {
|
||||||
|
const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
const [dismissed, setDismissed] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
async function check(): Promise<void> {
|
||||||
|
const talerUri = await findTalerUriInActiveTab();
|
||||||
|
if (talerUri) {
|
||||||
|
const actionUrl = actionForTalerUri(talerUri);
|
||||||
|
setTalerActionUrl(actionUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
if (talerActionUrl && !dismissed) {
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "1em" }}>
|
||||||
|
<h1>Taler Action</h1>
|
||||||
|
<p>This page has a Taler action. </p>
|
||||||
|
<p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
window.open(talerActionUrl, "_blank");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Open
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<button onClick={() => setDismissed(true)}>Dismiss</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<WalletNavBar />
|
<WalletNavBar />
|
||||||
@ -777,6 +884,6 @@ function WalletPopup(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPopup(): JSX.Element {
|
export function createPopup(): JSX.Element {
|
||||||
chrome.runtime.connect({ name: "popup" });
|
//chrome.runtime.connect({ name: "popup" });
|
||||||
return <WalletPopup />;
|
return <WalletPopup />;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,9 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { getDiagnostics } from "../wxApi";
|
import { getDiagnostics } from "../wxApi";
|
||||||
import { PageLink } from "../renderHtml";
|
import { PageLink } from "../renderHtml";
|
||||||
import { WalletDiagnostics } from "../../types/walletTypes";
|
import { WalletDiagnostics } from "../../types/walletTypes";
|
||||||
|
import * as wxApi from "../wxApi";
|
||||||
|
|
||||||
function Diagnostics(): JSX.Element {
|
function Diagnostics(): JSX.Element | null {
|
||||||
const [timedOut, setTimedOut] = useState(false);
|
const [timedOut, setTimedOut] = useState(false);
|
||||||
const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
|
const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
@ -55,7 +56,7 @@ function Diagnostics(): JSX.Element {
|
|||||||
|
|
||||||
if (diagnostics) {
|
if (diagnostics) {
|
||||||
if (diagnostics.errors.length === 0) {
|
if (diagnostics.errors.length === 0) {
|
||||||
return <p>Running diagnostics ... everything looks fine.</p>;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -96,16 +97,56 @@ function Diagnostics(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Welcome(): JSX.Element {
|
function Welcome(): JSX.Element {
|
||||||
|
const [extendedPermissions, setExtendedPermissions] = useState(false);
|
||||||
|
async function handleExtendedPerm(newVal: boolean): Promise<void> {
|
||||||
|
const res = await wxApi.setExtendedPermissions(newVal);
|
||||||
|
setExtendedPermissions(res.newValue);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
async function getExtendedPermValue(): Promise<void> {
|
||||||
|
const res = await wxApi.getExtendedPermissions()
|
||||||
|
setExtendedPermissions(res.newValue);
|
||||||
|
}
|
||||||
|
getExtendedPermValue();
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>Thank you for installing the wallet.</p>
|
<p>Thank you for installing the wallet.</p>
|
||||||
<h2>First Steps</h2>
|
|
||||||
<p>
|
|
||||||
Check out <a href="https://demo.taler.net/">demo.taler.net</a> for a
|
|
||||||
demo.
|
|
||||||
</p>
|
|
||||||
<h2>Troubleshooting</h2>
|
|
||||||
<Diagnostics />
|
<Diagnostics />
|
||||||
|
<h2>Permissions</h2>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
checked={extendedPermissions}
|
||||||
|
onChange={(x) => handleExtendedPerm(x.target.checked)}
|
||||||
|
type="checkbox"
|
||||||
|
id="checkbox-perm"
|
||||||
|
style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
htmlFor="checkbox-perm"
|
||||||
|
style={{ marginLeft: "0.5em", fontWeight: "bold" }}
|
||||||
|
>
|
||||||
|
Automatically open wallet based on page content
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: "#383838",
|
||||||
|
fontSize: "smaller",
|
||||||
|
display: "block",
|
||||||
|
marginLeft: "2em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
(Enabling this option below will make using the wallet faster, but
|
||||||
|
requires more permissions from your browser.)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h2>Next Steps</h2>
|
||||||
|
<a href="https://demo.taler.net/" style={{ display: "block" }}>
|
||||||
|
Try the demo »
|
||||||
|
</a>
|
||||||
|
<a href="https://demo.taler.net/" style={{ display: "block" }}>
|
||||||
|
Learn how to top up your wallet balance »
|
||||||
|
</a>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -160,11 +160,18 @@ function NewExchangeSelection(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<h1>Digital Cash Withdrawal</h1>
|
||||||
<i18n.Translate wrap="p">
|
<i18n.Translate wrap="p">
|
||||||
You are about to withdraw{" "}
|
You are about to withdraw{" "}
|
||||||
<strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from
|
<strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from
|
||||||
your bank account into your wallet.
|
your bank account into your wallet.
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
|
{ selectedExchange ?
|
||||||
|
<p>
|
||||||
|
The exchange <strong>{selectedExchange}</strong> will be used as the Taler payment service provider.
|
||||||
|
</p> : null
|
||||||
|
}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
className="pure-button button-success"
|
className="pure-button button-success"
|
||||||
|
@ -109,6 +109,7 @@ export class Collapsible extends React.Component<
|
|||||||
return (
|
return (
|
||||||
<h2>
|
<h2>
|
||||||
<a className="opener opener-collapsed" href="#" onClick={doOpen}>
|
<a className="opener opener-collapsed" href="#" onClick={doOpen}>
|
||||||
|
{" "}
|
||||||
{this.props.title}
|
{this.props.title}
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
@ -118,6 +119,7 @@ export class Collapsible extends React.Component<
|
|||||||
<div>
|
<div>
|
||||||
<h2>
|
<h2>
|
||||||
<a className="opener opener-open" href="#" onClick={doClose}>
|
<a className="opener opener-open" href="#" onClick={doClose}>
|
||||||
|
{" "}
|
||||||
{this.props.title}
|
{this.props.title}
|
||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
@ -143,7 +145,6 @@ function WireFee(props: {
|
|||||||
<th>Closing Fee</th>
|
<th>Closing Fee</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
,
|
|
||||||
<tbody>
|
<tbody>
|
||||||
{props.rci.wireFees.feesForType[props.s].map((f) => (
|
{props.rci.wireFees.feesForType[props.s].map((f) => (
|
||||||
<tr key={f.sig}>
|
<tr key={f.sig}>
|
||||||
@ -153,7 +154,6 @@ function WireFee(props: {
|
|||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
,
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
WithdrawDetails,
|
WithdrawDetails,
|
||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
AcceptWithdrawalResponse,
|
AcceptWithdrawalResponse,
|
||||||
|
ExtendedPermissionsResponse,
|
||||||
} from "../types/walletTypes";
|
} from "../types/walletTypes";
|
||||||
|
|
||||||
import { MessageMap, MessageType } from "./messages";
|
import { MessageMap, MessageType } from "./messages";
|
||||||
@ -324,3 +325,17 @@ export function acceptWithdrawal(
|
|||||||
export function getDiagnostics(): Promise<WalletDiagnostics> {
|
export function getDiagnostics(): Promise<WalletDiagnostics> {
|
||||||
return callBackend("get-diagnostics", {});
|
return callBackend("get-diagnostics", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get diagnostics information
|
||||||
|
*/
|
||||||
|
export function setExtendedPermissions(value: boolean): Promise<ExtendedPermissionsResponse> {
|
||||||
|
return callBackend("set-extended-permissions", { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get diagnostics information
|
||||||
|
*/
|
||||||
|
export function getExtendedPermissions(): Promise<ExtendedPermissionsResponse> {
|
||||||
|
return callBackend("get-extended-permissions", {});
|
||||||
|
}
|
||||||
|
@ -63,6 +63,11 @@ let outdatedDbVersion: number | undefined;
|
|||||||
|
|
||||||
const walletInit: OpenedPromise<void> = openPromise<void>();
|
const walletInit: OpenedPromise<void> = openPromise<void>();
|
||||||
|
|
||||||
|
const extendedPermissions = {
|
||||||
|
permissions: ["webRequest", "webRequestBlocking", "tabs"],
|
||||||
|
origins: ["http://*/*", "https://*/*"],
|
||||||
|
};
|
||||||
|
|
||||||
async function handleMessage(
|
async function handleMessage(
|
||||||
sender: MessageSender,
|
sender: MessageSender,
|
||||||
type: MessageType,
|
type: MessageType,
|
||||||
@ -282,6 +287,43 @@ async function handleMessage(
|
|||||||
}
|
}
|
||||||
case "prepare-pay":
|
case "prepare-pay":
|
||||||
return needsWallet().preparePayForUri(detail.talerPayUri);
|
return needsWallet().preparePayForUri(detail.talerPayUri);
|
||||||
|
case "set-extended-permissions": {
|
||||||
|
const newVal = detail.value;
|
||||||
|
if (newVal) {
|
||||||
|
const res = await new Promise((resolve, reject) => {
|
||||||
|
chrome.permissions.request(
|
||||||
|
extendedPermissions,
|
||||||
|
(granted: boolean) => {
|
||||||
|
console.log("permissions granted:", granted);
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error(chrome.runtime.lastError);
|
||||||
|
}
|
||||||
|
resolve(granted);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
setupHeaderListener();
|
||||||
|
}
|
||||||
|
return { newValue: res };
|
||||||
|
} else {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
chrome.permissions.remove(extendedPermissions, (rem) => {
|
||||||
|
console.log("permissions removed:", rem);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return { newVal: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "get-extended-permissions": {
|
||||||
|
const res = await new Promise((resolve, reject) => {
|
||||||
|
chrome.permissions.contains(extendedPermissions, (result: boolean) => {
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return { newValue: res };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// Exhaustiveness check.
|
// Exhaustiveness check.
|
||||||
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html
|
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html
|
||||||
@ -453,6 +495,91 @@ try {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
console.log(`got 402/202 from ${details.url}`);
|
||||||
|
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(
|
||||||
|
"withdraw.html",
|
||||||
|
details.tabId,
|
||||||
|
details.url,
|
||||||
|
{
|
||||||
|
talerWithdrawUri: talerUri,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TalerUriType.TalerPay:
|
||||||
|
return makeSyncWalletRedirect(
|
||||||
|
"pay.html",
|
||||||
|
details.tabId,
|
||||||
|
details.url,
|
||||||
|
{
|
||||||
|
talerPayUri: talerUri,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TalerUriType.TalerTip:
|
||||||
|
return makeSyncWalletRedirect(
|
||||||
|
"tip.html",
|
||||||
|
details.tabId,
|
||||||
|
details.url,
|
||||||
|
{
|
||||||
|
talerTipUri: talerUri,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TalerUriType.TalerRefund:
|
||||||
|
return makeSyncWalletRedirect(
|
||||||
|
"refund.html",
|
||||||
|
details.tabId,
|
||||||
|
details.url,
|
||||||
|
{
|
||||||
|
talerRefundUri: talerUri,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case TalerUriType.TalerNotifyReserve:
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
const w = currentWallet;
|
||||||
|
if (!w) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
w.handleNotifyReserve();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(
|
||||||
|
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupHeaderListener(): void {
|
||||||
|
// Handlers for catching HTTP requests
|
||||||
|
chrome.webRequest.onHeadersReceived.addListener(
|
||||||
|
headerListener,
|
||||||
|
{ urls: ["https://*/*", "http://*/*"] },
|
||||||
|
["responseHeaders", "blocking"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function to run for the WebExtension backend.
|
* Main function to run for the WebExtension backend.
|
||||||
*
|
*
|
||||||
@ -474,79 +601,5 @@ export async function wxMain(): Promise<void> {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handlers for catching HTTP requests
|
setupHeaderListener();
|
||||||
chrome.webRequest.onHeadersReceived.addListener(
|
|
||||||
(details) => {
|
|
||||||
const wallet = currentWallet;
|
|
||||||
if (!wallet) {
|
|
||||||
console.warn("wallet not available while handling header");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (details.statusCode === 402 || details.statusCode === 202) {
|
|
||||||
console.log(`got 402/202 from ${details.url}`);
|
|
||||||
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(
|
|
||||||
"withdraw.html",
|
|
||||||
details.tabId,
|
|
||||||
details.url,
|
|
||||||
{
|
|
||||||
talerWithdrawUri: talerUri,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case TalerUriType.TalerPay:
|
|
||||||
return makeSyncWalletRedirect(
|
|
||||||
"pay.html",
|
|
||||||
details.tabId,
|
|
||||||
details.url,
|
|
||||||
{
|
|
||||||
talerPayUri: talerUri,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case TalerUriType.TalerTip:
|
|
||||||
return makeSyncWalletRedirect(
|
|
||||||
"tip.html",
|
|
||||||
details.tabId,
|
|
||||||
details.url,
|
|
||||||
{
|
|
||||||
talerTipUri: talerUri,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case TalerUriType.TalerRefund:
|
|
||||||
return makeSyncWalletRedirect(
|
|
||||||
"refund.html",
|
|
||||||
details.tabId,
|
|
||||||
details.url,
|
|
||||||
{
|
|
||||||
talerRefundUri: talerUri,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case TalerUriType.TalerNotifyReserve:
|
|
||||||
Promise.resolve().then(() => {
|
|
||||||
const w = currentWallet;
|
|
||||||
if (!w) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
w.handleNotifyReserve();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.warn(
|
|
||||||
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
{ urls: ["https://*/*", "http://*/*"] },
|
|
||||||
["responseHeaders", "blocking"],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
|
"activeTab"
|
||||||
|
],
|
||||||
|
|
||||||
|
"optional_permissions": [
|
||||||
"tabs",
|
"tabs",
|
||||||
"webRequest",
|
"webRequest",
|
||||||
"webRequestBlocking",
|
"webRequestBlocking",
|
||||||
@ -39,16 +43,6 @@
|
|||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
|
|
||||||
"content_scripts": [
|
|
||||||
{
|
|
||||||
"matches": ["*://*/*"],
|
|
||||||
"js": [
|
|
||||||
"contentScript.js"
|
|
||||||
],
|
|
||||||
"run_at": "document_start"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"background": {
|
"background": {
|
||||||
"page": "background.html",
|
"page": "background.html",
|
||||||
"persistent": true
|
"persistent": true
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
body {
|
body {
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
margin-top: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main {
|
#main {
|
||||||
border: solid 1px black;
|
border: solid 5px black;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-top: 2em;
|
padding-top: 2em;
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
}
|
}
|
||||||
@ -18,16 +19,6 @@ header {
|
|||||||
height: 100px;
|
height: 100px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
font-size: 200%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 0 0 120px;
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header #logo {
|
header #logo {
|
||||||
@ -37,7 +28,6 @@ header #logo {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-right: 1px solid black;
|
|
||||||
background-image: url(/img/logo.png);
|
background-image: url(/img/logo.png);
|
||||||
background-size: 100px;
|
background-size: 100px;
|
||||||
}
|
}
|
||||||
@ -50,7 +40,6 @@ aside {
|
|||||||
section#main {
|
section#main {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-left: 1px solid black;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
}
|
}
|
||||||
@ -61,19 +50,23 @@ section#main h1:first-child {
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 160%;
|
font-size: 160%;
|
||||||
|
font-family: "monospace";
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 140%;
|
font-size: 140%;
|
||||||
|
font-family: "monospace";
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
|
font-family: "monospace";
|
||||||
}
|
}
|
||||||
|
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
|
font-family: "monospace";
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,3 +274,17 @@ a.opener {
|
|||||||
object.svg-icon.svg-baseline {
|
object.svg-icon.svg-baseline {
|
||||||
transform: translate(0, 0.125em);
|
transform: translate(0, 0.125em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide default HTML checkbox */
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Taler Wallet: Withdraw</title>
|
<title>Taler Wallet Installed</title>
|
||||||
|
|
||||||
<link rel="icon" href="/img/icon.png" />
|
<link rel="icon" href="/img/icon.png" />
|
||||||
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
|
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||||
@ -12,7 +12,12 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<section id="main">
|
<section id="main">
|
||||||
<h1>GNU Taler Wallet Installed!</h1>
|
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
|
||||||
|
<h1 style="font-family: monospace; font-size: 250%;">
|
||||||
|
<span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<h1>Browser Extension Installed!</h1>
|
||||||
<div id="container">Loading...</div>
|
<div id="container">Loading...</div>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<section id="main">
|
<section id="main">
|
||||||
<h1>GNU Taler Wallet</h1>
|
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
|
||||||
|
<h1 style="font-family: monospace; font-size: 250%;">
|
||||||
|
<span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
<div class="fade" id="container"></div>
|
<div class="fade" id="container"></div>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
Loading…
Reference in New Issue
Block a user