gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (b567ba466 -> f9d301866)


From: gnunet
Subject: [taler-wallet-core] branch master updated (b567ba466 -> f9d301866)
Date: Thu, 13 Apr 2023 17:58:02 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from b567ba466 wallet-core: work around missing timestamp in legacy 
transaction
     new 2baa42f22 show warning if bank settings are wrong
     new ebd004195 taler wallet interaction support, first version
     new 7b2de8944 use /taler-uri as redirection point to all taler uris
     new f9d301866 firefox support

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/demobank-ui/src/hooks/backend.ts          |  16 +-
 packages/demobank-ui/src/pages/AccountPage.tsx     |   8 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |  15 +-
 .../build-fast-with-linaria.mjs                    |   2 +
 .../taler-wallet-webextension/manifest-v2.json     |   9 +
 .../taler-wallet-webextension/manifest-v3.json     |   8 +-
 .../src/NavigationBar.tsx                          |  30 ++++
 .../src/platform/chrome.ts                         |  69 +++----
 .../src/taler-wallet-interaction-loader.ts         | 135 ++++++++++++++
 .../src/taler-wallet-interaction-support.ts        | 198 +++++++++++++++++++++
 .../src/wallet/Application.tsx                     |  65 ++++---
 .../taler-wallet-webextension/src/wxBackend.ts     |  74 ++------
 12 files changed, 484 insertions(+), 145 deletions(-)
 create mode 100644 
packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
 create mode 100644 
packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts

diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index e0b8d83ef..ca73a4793 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -53,10 +53,18 @@ interface LoggedOut {
 
 export function getInitialBackendBaseURL(): string {
   const overrideUrl = localStorage.getItem("bank-base-url");
-
-  return canonicalizeBaseUrl(
-    overrideUrl ? overrideUrl : bankUiSettings.backendBaseURL,
-  );
+  if (!overrideUrl) {
+    //normal path
+    if (!bankUiSettings.backendBaseURL) {
+      console.error(
+        "ERROR: backendBaseURL was overridden by a setting file and missing. 
Setting value to 'window.origin'",
+      );
+      return canonicalizeBaseUrl(window.origin);
+    }
+    return canonicalizeBaseUrl(bankUiSettings.backendBaseURL);
+  }
+  // testing/development path
+  return canonicalizeBaseUrl(overrideUrl);
 }
 
 export const defaultState: BackendState = {
diff --git a/packages/demobank-ui/src/pages/AccountPage.tsx 
b/packages/demobank-ui/src/pages/AccountPage.tsx
index bab8cca16..13960a8da 100644
--- a/packages/demobank-ui/src/pages/AccountPage.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage.tsx
@@ -20,15 +20,13 @@ import {
   HttpResponsePaginated,
   useTranslationContext,
 } from "@gnu-taler/web-util/lib/index.browser";
-import { Fragment, h, VNode } from "preact";
-import { Loading } from "../components/Loading.js";
+import { Fragment, VNode, h } from "preact";
 import { Transactions } from "../components/Transactions/index.js";
-import { PageStateType, notifyError } from "../context/pageState.js";
+import { useBackendContext } from "../context/backend.js";
+import { notifyError } from "../context/pageState.js";
 import { useAccountDetails } from "../hooks/access.js";
 import { LoginForm } from "./LoginForm.js";
 import { PaymentOptions } from "./PaymentOptions.js";
-import { StateUpdater } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
 
 interface Props {
   account: string;
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index 8613bfca7..919f2b1fe 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -48,20 +48,9 @@ export function QrCodeSection({
           <p>
             <i18n.Translate>
               Click{" "}
-              <a
-                id="linkqr"
-                href={talerWithdrawUri}
-              >{i18n.str`this taler:// link`}</a>{" "}
-              to open your Taler wallet
+              <a href={talerWithdrawUri}>{i18n.str`this taler:// link`}</a> to
+              open your Taler wallet
             </i18n.Translate>{" "}
-            <i18n.Translate>
-              or try{" "}
-              <a
-                id="linkqr"
-                href={"ext+" + talerWithdrawUri}
-              >{i18n.str`this ext+taler:// link`}</a>{" "}
-              if your browser does not support <b>taler://</b> scheme.
-            </i18n.Translate>
           </p>
           <br />
           <a
diff --git a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs 
b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
index 1232eac98..44e502b6a 100755
--- a/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
+++ b/packages/taler-wallet-webextension/build-fast-with-linaria.mjs
@@ -60,6 +60,8 @@ const entryPoints = [
   'src/background.ts',
   'src/stories.tsx',
   'src/background.dev.ts',
+  'src/taler-wallet-interaction-loader.ts',
+  'src/taler-wallet-interaction-support.ts',
   'src/browserWorkerEntry.ts'
 ]
 
diff --git a/packages/taler-wallet-webextension/manifest-v2.json 
b/packages/taler-wallet-webextension/manifest-v2.json
index 6adadad98..794aac3b1 100644
--- a/packages/taler-wallet-webextension/manifest-v2.json
+++ b/packages/taler-wallet-webextension/manifest-v2.json
@@ -21,11 +21,20 @@
     "https://*/*";,
     "activeTab"
   ],
+  "web_accessible_resources": [
+    "static/wallet.html",
+    "dist/taler-wallet-interaction-support.js"
+  ],
   "optional_permissions": [
     "http://*/*";,
     "https://*/*";,
     "webRequest"
   ],
+  "content_scripts": [{
+    "id": "taler-wallet-interaction-support",
+    "matches": ["file://*/*", "http://*/*";, "https://*/*";],
+    "js": ["dist/taler-wallet-interaction-loader.js"]
+  }],
   "protocol_handlers": [
     {
       "protocol": "ext+taler",
diff --git a/packages/taler-wallet-webextension/manifest-v3.json 
b/packages/taler-wallet-webextension/manifest-v3.json
index 4e18125b3..68b3e23ee 100644
--- a/packages/taler-wallet-webextension/manifest-v3.json
+++ b/packages/taler-wallet-webextension/manifest-v3.json
@@ -29,10 +29,16 @@
   "optional_permissions": [
     "webRequest"
   ],
+  "content_scripts": [{
+    "id": "taler-wallet-interaction",
+    "matches": ["file://*/*", "http://*/*";, "https://*/*";],
+    "js": ["dist/taler-wallet-interaction-loader.js"]
+  }],
   "web_accessible_resources": [
     {
       "resources": [
-        "static/wallet.html"
+        "static/wallet.html",
+        "dist/taler-wallet-interaction-support.js"
       ],
       "matches": [
         "https://*/*";,
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index e8ee4f475..b73704c70 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -38,6 +38,7 @@ import { useAsyncAsHook } from "./hooks/useAsyncAsHook.js";
 import qrIcon from "./svg/qr_code_24px.svg";
 import settingsIcon from "./svg/settings_black_24dp.svg";
 import warningIcon from "./svg/warning_24px.svg";
+import { parseTalerUri, TalerUriAction } from "@gnu-taler/taler-util";
 
 /**
  * List of pages used by the wallet
@@ -115,6 +116,7 @@ export const Pages = {
     "/settings/exchange/add/:currency?",
   ),
 
+  defaultCta: pageDefinition<{ uri: string }>("/taler-uri/:uri"),
   cta: pageDefinition<{ action: string }>("/cta/:action"),
   ctaPay: "/cta/pay",
   ctaPayTemplate: "/cta/pay/template",
@@ -136,6 +138,34 @@ export const Pages = {
   ),
 };
 
+const talerUriActionToPageName: {
+  [t in TalerUriAction]: keyof typeof Pages | undefined;
+} = {
+  [TalerUriAction.Withdraw]: "ctaWithdraw",
+  [TalerUriAction.Pay]: "ctaPay",
+  [TalerUriAction.Tip]: "ctaTips",
+  [TalerUriAction.Refund]: "ctaRefund",
+  [TalerUriAction.PayPull]: "ctaInvoicePay",
+  [TalerUriAction.PayPush]: "ctaTransferPickup",
+  [TalerUriAction.Restore]: "ctaRecovery",
+  [TalerUriAction.PayTemplate]: "ctaPayTemplate",
+  [TalerUriAction.DevExperiment]: undefined,
+  [TalerUriAction.Exchange]: undefined,
+  [TalerUriAction.Auditor]: undefined,
+};
+
+export function getPathnameForTalerURI(talerUri: string): string | undefined {
+  const uri = parseTalerUri(talerUri);
+  if (!uri) {
+    return undefined;
+  }
+  const pageName = talerUriActionToPageName[uri.type];
+  if (!pageName) {
+    return undefined;
+  }
+  return `${Pages[pageName]}?talerUri=${encodeURIComponent(talerUri)}`;
+}
+
 export type PopupNavBarOptions = "balance" | "backup" | "dev";
 export function PopupNavBar({ path }: { path?: PopupNavBarOptions }): VNode {
   const api = useBackendContext();
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 4b0bdbfb7..03259314e 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -15,11 +15,11 @@
  */
 
 import {
-  classifyTalerUri,
   Logger,
   TalerErrorCode,
-  TalerUriType,
+  TalerUriAction,
   TalerError,
+  parseTalerUri,
 } from "@gnu-taler/taler-util";
 import { WalletOperations } from "@gnu-taler/taler-wallet-core";
 import { BackgroundOperations } from "../wxApi.js";
@@ -239,80 +239,83 @@ function openWalletURIFromPopup(maybeTalerUri: string): 
void {
   const talerUri = maybeTalerUri.startsWith("ext+")
     ? maybeTalerUri.substring(4)
     : maybeTalerUri;
-  const uriType = classifyTalerUri(talerUri);
+  const uri = parseTalerUri(talerUri);
+  if (!uri) {
+    logger.warn(
+      `Response with HTTP 402 the Taler header but could not classify 
${talerUri}`,
+    );
+    return;
+  }
+  //FIXME: this should redirect to just one place
+  // the target pathname should handle what happens if the endpoint is not 
there
+  // like "trying to open from popup but this uri is not handled"
 
   encodeURIComponent;
   let url: string | undefined = undefined;
-  switch (uriType) {
-    case TalerUriType.TalerWithdraw:
+  switch (uri.type) {
+    case TalerUriAction.Withdraw:
       url = chrome.runtime.getURL(
-        
`static/wallet.html#/cta/withdraw?talerWithdrawUri=${encodeURIComponent(
+        `static/wallet.html#/cta/withdraw?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.TalerRecovery:
+    case TalerUriAction.Restore:
       url = chrome.runtime.getURL(
-        
`static/wallet.html#/cta/recovery?talerRecoveryUri=${encodeURIComponent(
+        `static/wallet.html#/cta/recovery?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.TalerPay:
+    case TalerUriAction.Pay:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/pay?talerPayUri=${encodeURIComponent(
-          talerUri,
-        )}`,
+        `static/wallet.html#/cta/pay?talerUri=${encodeURIComponent(talerUri)}`,
       );
       break;
-    case TalerUriType.TalerTip:
+    case TalerUriAction.Tip:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/tip?talerTipUri=${encodeURIComponent(
-          talerUri,
-        )}`,
+        `static/wallet.html#/cta/tip?talerUri=${encodeURIComponent(talerUri)}`,
       );
       break;
-    case TalerUriType.TalerRefund:
+    case TalerUriAction.Refund:
       url = chrome.runtime.getURL(
-        `static/wallet.html#/cta/refund?talerRefundUri=${encodeURIComponent(
+        `static/wallet.html#/cta/refund?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.TalerPayPull:
+    case TalerUriAction.PayPull:
       url = chrome.runtime.getURL(
-        
`static/wallet.html#/cta/invoice/pay?talerPayPullUri=${encodeURIComponent(
+        `static/wallet.html#/cta/invoice/pay?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.TalerPayPush:
+    case TalerUriAction.PayPush:
       url = chrome.runtime.getURL(
-        
`static/wallet.html#/cta/transfer/pickup?talerPayPushUri=${encodeURIComponent(
+        `static/wallet.html#/cta/transfer/pickup?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.TalerPayTemplate:
+    case TalerUriAction.PayTemplate:
       url = chrome.runtime.getURL(
-        
`static/wallet.html#/cta/pay/template?talerPayTemplateUri=${encodeURIComponent(
+        `static/wallet.html#/cta/pay/template?talerUri=${encodeURIComponent(
           talerUri,
         )}`,
       );
       break;
-    case TalerUriType.Unknown:
-      logger.warn(
-        `Response with HTTP 402 the Taler header but could not classify 
${talerUri}`,
-      );
-      return;
-    case TalerUriType.TalerDevExperiment:
+    case TalerUriAction.DevExperiment:
       logger.warn(`taler://dev-experiment URIs are not allowed in headers`);
       return;
-    case TalerUriType.TalerTemplate:
-      logger.warn(`taler://dev-template URIs are not allowed in headers`);
+    case TalerUriAction.Exchange:
+      logger.warn(`taler://exchange not yet supported`);
+      return;
+    case TalerUriAction.Auditor:
+      logger.warn(`taler://auditor not yet supported`);
       return;
     default: {
-      const error: never = uriType;
+      const error: never = uri;
       logger.warn(
         `Response with HTTP 402 the Taler header "${error}", but header value 
is not a taler:// URI.`,
       );
diff --git 
a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts 
b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
new file mode 100644
index 000000000..838b47397
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
@@ -0,0 +1,135 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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/>
+ */
+
+/**
+ * This will modify all the pages that the user load when navigating with Web 
Extension enabled
+ *
+ * Can't do useful integration since it run in ISOLATED (or equivalent) mode.
+ *
+ * If taler support is expected, it will inject a script which will complete 
the integration.
+ */
+
+// 
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Chrome_incompatibilities#content_script_environment
+
+// ISOLATED mode in chromium browsers
+// 
https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/bindings/core/v8/V8BindingDesign.md#world
+// X-Ray vision in Firefox
+// 
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts#xray_vision_in_firefox
+
+// *** IMPORTANT ***
+
+// Content script lifecycle during navigation
+// In Firefox: Content scripts remain injected in a web page after the user 
has navigated away,
+// however, window object properties are destroyed.
+// In Chrome: Content scripts are destroyed when the user navigates away from 
a web page.
+
+const documentDocTypeIsHTML =
+  window.document.doctype && window.document.doctype.name === "html";
+const suffixIsNotXMLorPDF =
+  !window.location.pathname.endsWith(".xml") &&
+  !window.location.pathname.endsWith(".pdf");
+const rootElementIsHTML =
+  document.documentElement.nodeName &&
+  document.documentElement.nodeName.toLowerCase() === "html";
+const pageAcceptsTalerSupport = document.head.querySelector(
+  "meta[name=taler-support]",
+);
+// safe check, if one of this is true then taler handler is not useful
+// or not expected
+const shouldNotInject =
+  !documentDocTypeIsHTML ||
+  !suffixIsNotXMLorPDF ||
+  // !pageAcceptsTalerSupport || FIXME: removing this before release for 
testing
+  !rootElementIsHTML;
+const logger = {
+  debug: (...msg: any[]) => {},
+  info: (...msg: any[]) =>
+    console.log(`${new Date().toISOString()} TALER`, ...msg),
+  error: (...msg: any[]) =>
+    console.error(`${new Date().toISOString()} TALER`, ...msg),
+};
+
+function start() {
+  if (shouldNotInject) {
+    return;
+  }
+  const debugEnabled =
+    pageAcceptsTalerSupport?.getAttribute("debug") === "true";
+  if (debugEnabled) {
+    logger.debug = logger.info;
+  }
+  createBridgeWithExtension();
+  logger.debug("bridged created");
+  injectTalerSupportScript(debugEnabled);
+  logger.debug("done");
+}
+
+/**
+ * Create a <script /> element that load the support in the page context.
+ * The interaction support script will create the API to send message
+ * that will be received by this loader and be redirected to the extension
+ * using the bridge.
+ */
+function injectTalerSupportScript(debugEnabled: boolean) {
+  const container = document.head || document.documentElement;
+  const scriptTag = document.createElement("script");
+
+  scriptTag.setAttribute("async", "false");
+  const url = new URL(
+    chrome.runtime.getURL("/dist/taler-wallet-interaction-support.js"),
+  );
+  url.searchParams.set("id", chrome.runtime.id);
+  if (debugEnabled) {
+    url.searchParams.set("debug", "true");
+  }
+  scriptTag.src = url.href;
+  try {
+    container.insertBefore(scriptTag, container.children[0]);
+  } catch (e) {
+    logger.info("inserting link handler failed!");
+    logger.error(e);
+  }
+}
+
+/**
+ * Create a bridge connection between the page and the extension.
+ *
+ * Useful for API calls and replies. Not yet supported.
+ */
+function createBridgeWithExtension() {
+  const port = chrome.runtime.connect();
+
+  window.addEventListener(
+    "message",
+    (event) => {
+      logger.debug("message received", event);
+      if (event.source !== window) {
+        return;
+      }
+      if (event.origin !== window.origin) {
+        return;
+      }
+
+      if (event.data.type && event.data.type === "FROM_PAGE") {
+        logger.debug("Content script received: " + event.data.text);
+        port.postMessage(event.data.text);
+      }
+    },
+    false,
+  );
+}
+
+start();
diff --git 
a/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts 
b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
new file mode 100644
index 000000000..7f706e815
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-support.ts
@@ -0,0 +1,198 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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/>
+ */
+
+/**
+ * WARNING
+ *
+ * This script will be loaded and run in every page while the
+ * user us navigating. It must be short, simple and safe.
+ */
+
+const logger = {
+  debug: (...msg: any[]) => {},
+  info: (...msg: any[]) =>
+    console.log(`${new Date().toISOString()} TALER`, ...msg),
+  error: (...msg: any[]) =>
+    console.error(`${new Date().toISOString()} TALER`, ...msg),
+};
+
+const documentDocTypeIsHTML =
+  window.document.doctype && window.document.doctype.name === "html";
+const suffixIsNotXMLorPDF =
+  !window.location.pathname.endsWith(".xml") &&
+  !window.location.pathname.endsWith(".pdf");
+const rootElementIsHTML =
+  document.documentElement.nodeName &&
+  document.documentElement.nodeName.toLowerCase() === "html";
+const pageAcceptsTalerSupport = document.head.querySelector(
+  "meta[name=taler-support]",
+);
+
+// this is also checked by the loader
+// but a double check will prevent running and breaking user navigation
+// if loaded from other location
+const shouldNotRun =
+  !documentDocTypeIsHTML ||
+  !suffixIsNotXMLorPDF ||
+  // !pageAcceptsTalerSupport || FIXME: removing this before release for 
testing
+  !rootElementIsHTML;
+
+interface Info {
+  extensionId: string;
+  protocol: string;
+  hostname: string;
+}
+interface API {
+  convertURIToWebExtensionPath: (uri: string) => string | undefined;
+  anchorOnClick: (ev: MouseEvent) => void;
+  registerProtocolHandler: () => void;
+}
+interface TalerSupport {
+  info: Readonly<Info>;
+  api: API;
+}
+
+function buildApi(config: Readonly<Info>): API {
+  /**
+   * Takes an anchor href that starts with taler:// and
+   * returns the path to the web-extension page
+   */
+  function convertURIToWebExtensionPath(uri: string): string | undefined {
+    if (!validateTalerUri(uri)) {
+      logger.error(`taler:// URI is invalid: ${uri}`);
+      return undefined;
+    }
+    const host = `${config.protocol}//${config.hostname}`;
+    const path = `static/wallet.html#/taler-uri/${encodeURIComponent(uri)}`;
+    return `${host}/${path}`;
+  }
+
+  function anchorOnClick(ev: MouseEvent) {
+    if (!(ev.currentTarget instanceof Element)) {
+      logger.debug(`onclick: registered in a link that is not an HTML 
element`);
+      return;
+    }
+    const hrefAttr = ev.currentTarget.attributes.getNamedItem("href");
+    if (!hrefAttr) {
+      logger.debug(`onclick: link didn't have href with taler:// uri`);
+      return;
+    }
+    const targetAttr = ev.currentTarget.attributes.getNamedItem("target");
+    const windowTarget =
+      targetAttr && targetAttr.value ? targetAttr.value : "taler-wallet";
+    const page = convertURIToWebExtensionPath(hrefAttr.value);
+    if (!page) {
+      logger.debug(`onclick: could not convert "${hrefAttr.value}" into path`);
+      return;
+    }
+    // we can use window.open, but maybe some browser will block it?
+    window.open(page, windowTarget);
+    ev.preventDefault();
+    ev.stopPropagation();
+    ev.stopImmediatePropagation();
+    // another possibility is to change the location when the click is made
+    // or when the anchor is found
+    // hrefAttr.value = page
+    // TODO: explore different possibilities and maybe allow the configuration
+    // using the meta-tag
+    return false;
+  }
+
+  function overrideAllAnchor(root: HTMLElement) {
+    const allAnchors = root.querySelectorAll("a[href^=taler]");
+    logger.debug(`registering taler protocol in ${allAnchors.length} links`);
+    allAnchors.forEach((link) => {
+      if (link instanceof HTMLElement) {
+        link.addEventListener("click", anchorOnClick);
+      }
+    });
+  }
+
+  function checkForNewAnchors(
+    mutations: MutationRecord[],
+    observer: MutationObserver,
+  ) {
+    mutations.forEach((mut) => {
+      if (mut.type === "childList") {
+        mut.addedNodes.forEach((added) => {
+          if (added instanceof HTMLElement) {
+            logger.debug(`new element`, added);
+            overrideAllAnchor(added);
+          }
+        });
+      }
+    });
+  }
+
+  /**
+   * Check of every anchor and observes for new one.
+   * Register the anchor handler when found
+   */
+  function registerProtocolHandler() {
+    const observer = new MutationObserver(checkForNewAnchors);
+    observer.observe(document.body, {
+      childList: true,
+      subtree: true,
+      attributes: false,
+    });
+
+    overrideAllAnchor(document.body);
+  }
+
+  return {
+    convertURIToWebExtensionPath,
+    anchorOnClick,
+    registerProtocolHandler,
+  };
+}
+
+function start() {
+  if (shouldNotRun) return;
+  if (!(document.currentScript instanceof HTMLScriptElement)) return;
+
+  const url = new URL(document.currentScript.src);
+  const { protocol, searchParams, hostname } = url;
+  const extensionId = searchParams.get("id") ?? "";
+  const debugEnabled = searchParams.get("debug") === "true";
+  if (debugEnabled) {
+    logger.debug = logger.info;
+  }
+
+  const info: Info = Object.freeze({
+    extensionId,
+    protocol,
+    hostname,
+  });
+  const taler: TalerSupport = {
+    info,
+    api: buildApi(info),
+  };
+
+  //@ts-ignore
+  window.taler = taler;
+
+  //default behavior: register on install
+  taler.api.registerProtocolHandler();
+}
+
+// utils functions
+function validateTalerUri(uri: string): boolean {
+  return (
+    !!uri && (uri.startsWith("taler://") || uri.startsWith("taler+http://";))
+  );
+}
+
+start();
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 4a5ef30eb..2b03bb947 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -20,7 +20,11 @@
  * @author sebasjm
  */
 
-import { TranslatedString } from "@gnu-taler/taler-util";
+import {
+  TalerUriAction,
+  TranslatedString,
+  parseTalerUri,
+} from "@gnu-taler/taler-util";
 import { createHashHistory } from "history";
 import { ComponentChildren, Fragment, h, VNode } from "preact";
 import { route, Route, Router } from "preact-router";
@@ -55,7 +59,12 @@ import {
   WithdrawPageFromParams,
   WithdrawPageFromURI,
 } from "../cta/Withdraw/index.js";
-import { Pages, WalletNavBar, WalletNavBarOptions } from "../NavigationBar.js";
+import {
+  Pages,
+  WalletNavBar,
+  WalletNavBarOptions,
+  getPathnameForTalerURI,
+} from "../NavigationBar.js";
 import { platform } from "../platform/foreground.js";
 import CloseIcon from "../svg/close_24px.svg";
 import { AddBackupProviderPage } from "./AddBackupProvider/index.js";
@@ -285,12 +294,22 @@ export function Application(): VNode {
             {/**
              * CALL TO ACTION
              */}
+            <Route
+              path={Pages.defaultCta.pattern}
+              component={({ uri }: { uri: string }) => {
+                const path = getPathnameForTalerURI(uri);
+                if (!path) {
+                  return <Redirect to={Pages.balance} />;
+                }
+                return <Redirect to={path} />;
+              }}
+            />
             <Route
               path={Pages.ctaPay}
-              component={({ talerPayUri }: { talerPayUri: string }) => (
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash payment`}>
                   <PaymentPage
-                    talerPayUri={decodeURIComponent(talerPayUri)}
+                    talerPayUri={decodeURIComponent(talerUri)}
                     goToWalletManualWithdraw={(amount?: string) =>
                       redirectTo(Pages.receiveCash({ amount }))
                     }
@@ -304,14 +323,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaPayTemplate}
-              component={({
-                talerPayTemplateUri,
-              }: {
-                talerPayTemplateUri: string;
-              }) => (
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash payment`}>
                   <PaymentTemplatePage
-                    talerTemplateUri={decodeURIComponent(talerPayTemplateUri)}
+                    talerTemplateUri={decodeURIComponent(talerUri)}
                     goToWalletManualWithdraw={(amount?: string) =>
                       redirectTo(Pages.receiveCash({ amount }))
                     }
@@ -325,10 +340,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaRefund}
-              component={({ talerRefundUri }: { talerRefundUri: string }) => (
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash refund`}>
                   <RefundPage
-                    talerRefundUri={decodeURIComponent(talerRefundUri)}
+                    talerRefundUri={decodeURIComponent(talerUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -339,10 +354,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaTips}
-              component={({ talerTipUri }: { talerTipUri: string }) => (
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash tip`}>
                   <TipPage
-                    talerTipUri={decodeURIComponent(talerTipUri)}
+                    talerTipUri={decodeURIComponent(talerUri)}
                     onCancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -353,14 +368,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaWithdraw}
-              component={({
-                talerWithdrawUri,
-              }: {
-                talerWithdrawUri: string;
-              }) => (
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash 
withdrawal`}>
                   <WithdrawPageFromURI
-                    talerWithdrawUri={decodeURIComponent(talerWithdrawUri)}
+                    talerWithdrawUri={decodeURIComponent(talerUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -387,15 +398,15 @@ export function Application(): VNode {
               path={Pages.ctaDeposit}
               component={({
                 amount,
-                talerDepositUri,
+                talerUri,
               }: {
                 amount: string;
-                talerDepositUri: string;
+                talerUri: string;
               }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash deposit`}>
                   <DepositPageCTA
                     amountStr={amount}
-                    talerDepositUri={decodeURIComponent(talerDepositUri)}
+                    talerDepositUri={decodeURIComponent(talerUri)}
                     cancel={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
@@ -434,10 +445,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaInvoicePay}
-              component={({ talerPayPullUri }: { talerPayPullUri: string }) => 
(
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash invoice`}>
                   <InvoicePayPage
-                    talerPayPullUri={decodeURIComponent(talerPayPullUri)}
+                    talerPayPullUri={decodeURIComponent(talerUri)}
                     goToWalletManualWithdraw={(amount?: string) =>
                       redirectTo(Pages.receiveCash({ amount }))
                     }
@@ -451,10 +462,10 @@ export function Application(): VNode {
             />
             <Route
               path={Pages.ctaTransferPickup}
-              component={({ talerPayPushUri }: { talerPayPushUri: string }) => 
(
+              component={({ talerUri }: { talerUri: string }) => (
                 <CallToActionTemplate title={i18n.str`Digital cash transfer`}>
                   <TransferPickupPage
-                    talerPayPushUri={decodeURIComponent(talerPayPushUri)}
+                    talerPayPushUri={decodeURIComponent(talerUri)}
                     onClose={() => redirectTo(Pages.balance)}
                     onSuccess={(tid: string) =>
                       redirectTo(Pages.balanceTransaction({ tid }))
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts 
b/packages/taler-wallet-webextension/src/wxBackend.ts
index 3655c5dbc..c50412053 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -34,6 +34,8 @@ import {
   WalletDiagnostics,
   makeErrorDetail,
   getErrorDetailFromException,
+  parseTalerUri,
+  TalerUriAction,
 } from "@gnu-taler/taler-util";
 import {
   DbAccess,
@@ -332,69 +334,17 @@ function parseTalerUriAndRedirect(tabId: number, 
maybeTalerUri: string): void {
   const talerUri = maybeTalerUri.startsWith("ext+")
     ? maybeTalerUri.substring(4)
     : maybeTalerUri;
-  const uriType = classifyTalerUri(talerUri);
-  switch (uriType) {
-    case TalerUriType.TalerWithdraw:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/withdraw?talerWithdrawUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerPay:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/pay?talerPayUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerTip:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/tip?talerTipUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerRefund:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/refund?talerRefundUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerPayPull:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/invoice/pay?talerPayPullUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerPayPush:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        `/cta/transfer/pickup?talerPayPushUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerRecovery:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        
`/cta/transfer/recovery?talerBackupUri=${encodeURIComponent(talerUri)}`,
-      );
-    case TalerUriType.TalerPayTemplate:
-      return platform.redirectTabToWalletPage(
-        tabId,
-        
`/cta/pay/template?talerPayTemplateUri=${encodeURIComponent(talerUri)}`,
-      );
-      return;
-    case TalerUriType.Unknown:
-      logger.warn(
-        `Response with HTTP 402 the Taler header but could not classify 
${talerUri}`,
-      );
-      return;
-    case TalerUriType.TalerDevExperiment:
-      // FIXME: Implement!
-      logger.warn("not implemented");
-      return;
-    case TalerUriType.TalerTemplate:
-      logger.warn("not implemented");
-      return;
-    default: {
-      const error: never = uriType;
-      logger.warn(
-        `Response with HTTP 402 the Taler header "${error}", but header value 
is not a taler:// URI.`,
-      );
-      return;
-    }
+  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)}`,
+  );
 }
 
 /**

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]