diff options
| author | Florian Dold <florian.dold@gmail.com> | 2016-09-23 14:09:07 +0200 | 
|---|---|---|
| committer | Florian Dold <florian.dold@gmail.com> | 2016-09-23 14:09:18 +0200 | 
| commit | 5737c121ca3635a4afee0c017b7652eb39ee4ab1 (patch) | |
| tree | 61bd0cbab6b85d6c1ab102940fa8cf57f6aafa2f | |
| parent | d705309199a38cea944583238270a52b8b74204b (diff) | |
use uniform DOM event handling mechanism
| -rw-r--r-- | content_scripts/notify.ts | 260 | ||||
| -rw-r--r-- | lib/wallet/wallet.ts | 8 | ||||
| -rw-r--r-- | lib/wallet/wxMessaging.ts | 29 | ||||
| -rw-r--r-- | manifest.json | 1 | 
4 files changed, 103 insertions, 195 deletions
| diff --git a/content_scripts/notify.ts b/content_scripts/notify.ts index 7e54f27d6..8d8d21ab6 100644 --- a/content_scripts/notify.ts +++ b/content_scripts/notify.ts @@ -24,6 +24,7 @@  /// <reference path="../lib/decl/chrome/chrome.d.ts" /> +/// <reference path="../lib/decl/urijs/URIjs.d.ts" />  "use strict"; @@ -31,128 +32,10 @@  namespace TalerNotify {    const PROTOCOL_VERSION = 1; -  /** -   * Wallet-internal version of offerContractFrom, used for 402 payments. -   */ -  function internalOfferContractFrom(url: string) { -    function handle_contract(contract_wrapper: any) { -      var cEvent = new CustomEvent("taler-confirm-contract", { -        detail: { -          contract_wrapper: contract_wrapper, -          replace_navigation: true -        } -      }); -      document.dispatchEvent(cEvent); -    } - -    var contract_request = new XMLHttpRequest(); -    console.log("downloading contract from '" + url + "'"); -    contract_request.open("GET", url, true); -    contract_request.onload = function (e) { -      if (contract_request.readyState == 4) { -        if (contract_request.status == 200) { -          console.log("response text:", -                      contract_request.responseText); -          var contract_wrapper = JSON.parse(contract_request.responseText); -          if (!contract_wrapper) { -            console.error("response text was invalid json"); -            alert("Failure to download contract (invalid json)"); -            return; -          } -          handle_contract(contract_wrapper); -        } else { -          alert("Failure to download contract from merchant " + -                "(" + contract_request.status + "):\n" + -                contract_request.responseText); -        } -      } -    }; -    contract_request.onerror = function (e) { -      alert("Failure requesting the contract:\n" -            + contract_request.statusText); -    }; -    contract_request.send(); -  } - -  /** -   * Wallet-internal version of executeContract, used for 402 payments. -   * -   * Even though we're inside a content script, we send events to the dom -   * to avoid code duplication. -   */ -  function internalExecuteContract(contractHash: string, payUrl: string, -                           offerUrl: string) { -    /** -     * Handle a failed payment. -     * -     * Try to notify the wallet first, before we show a potentially -     * synchronous error message (such as an alert) or leave the page. -     */ -    function handleFailedPayment(status: any) { -      const msg = { -        type: "payment-failed", -        detail: {}, -      }; -      chrome.runtime.sendMessage(msg, (resp) => { -        alert("payment failed"); -      }); -    } - - -    function handleResponse(evt: CustomEvent) { -      console.log("handling taler-notify-payment"); -      // Payment timeout in ms. -      let timeout_ms = 1000; -      // Current request. -      let r: XMLHttpRequest | null = null; -      let timeoutHandle: number|null = null; -      function sendPay() { -        r = new XMLHttpRequest(); -        r.open("post", payUrl); -        r.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); -        r.send(JSON.stringify(evt.detail.payment)); -        r.onload = function() { -          if (!r) { -            throw Error("invariant"); -          } -          switch (r.status) { -            case 200: -              window.location.href = subst(evt.detail.contract.fulfillment_url, -                                           evt.detail.H_contract); -              window.location.reload(true); -              break; -            default: -              handleFailedPayment(r.status); -              break; -          } -          r = null; -          if (timeoutHandle != null) { -            clearTimeout(timeoutHandle); -            timeoutHandle = null; -          } -        }; -        function retry() { -          if (r) { -            r.abort(); -            r = null; -          } -          timeout_ms = Math.min(timeout_ms * 2, 10 * 1000); -          console.log("sendPay timed out, retrying in ", timeout_ms, "ms"); -          sendPay(); -        } -        timeoutHandle = setTimeout(retry, timeout_ms); -      } -      sendPay(); -    } - -    let detail = { -      H_contract: contractHash, -      offering_url: offerUrl -    }; - -    document.addEventListener("taler-notify-payment", handleResponse, false); -    let eve = new CustomEvent('taler-execute-contract', {detail: detail}); -    document.dispatchEvent(eve); +  let taler: any; +  if (!taler) { +    console.error("Taler wallet lib not included, HTTP 402 payments not" + +                  " supported");    }    function subst(url: string, H_contract: string) { @@ -185,17 +68,17 @@ namespace TalerNotify {          }        }); - -        if (resp && resp.type === "fetch") {          console.log("it's fetch"); -        internalOfferContractFrom(resp.contractUrl); +        taler.internalOfferContractFrom(resp.contractUrl);          document.documentElement.style.visibility = "hidden";        } else if (resp && resp.type === "execute") {          console.log("it's execute");          document.documentElement.style.visibility = "hidden"; -        internalExecuteContract(resp.contractHash, resp.payUrl, resp.offerUrl); +        taler.internalExecuteContract(resp.contractHash, +                                      resp.payUrl, +                                      resp.offerUrl);        }      });    } @@ -203,78 +86,89 @@ namespace TalerNotify {    console.log("loading Taler content script");    init(); +  interface HandlerFn { +    (detail: any, sendResponse: (msg: any) => void): void; +  } +    function registerHandlers() { -    function addHandler(type: string, listener: (e: CustomEvent) => void) { -      document.addEventListener(type, listener); -      handlers.push({type, listener}); +    /** +     * Add a handler for a DOM event, which automatically +     * handles adding sequence numbers to responses. +     */ +    function addHandler(type: string, handler: HandlerFn) { +      let handlerWrap = (e: CustomEvent) => { +        let callId: number|undefined = e.detail.callId; +        let responder = (msg?: any) => { +          let fullMsg = Object.assign({}, msg, {callId}); +          let evt = new CustomEvent(type + "-result", {detail: fullMsg}); +          document.dispatchEvent(evt); +        }; +        handler(e, responder); +      }; +      document.addEventListener(type, handlerWrap); +      handlers.push({type, listener: handlerWrap});      } -    addHandler("taler-query-id", function(e) { -      let evt = new CustomEvent("taler-id", { -        detail: { -          id: chrome.runtime.id -        } -      }); -      document.dispatchEvent(evt); + +    addHandler("taler-query-id", (msg: any, sendResponse: any) => { +      // FIXME: maybe include this info in taoer-probe? +      sendResponse({id: chrome.runtime.id})      }); -    addHandler("taler-probe", function(e) { -      let evt = new CustomEvent("taler-wallet-present", { -        detail: { -          walletProtocolVersion: PROTOCOL_VERSION -        } -      }); -      document.dispatchEvent(evt); +    addHandler("taler-probe", (msg: any, sendResponse: any) => { +      sendResponse();      }); -    addHandler("taler-create-reserve", function(e: CustomEvent) { -      console.log("taler-create-reserve with " + JSON.stringify(e.detail)); +    addHandler("taler-create-reserve", (msg: any) => { +      console.log("taler-create-reserve with " + JSON.stringify(msg));        let params = { -        amount: JSON.stringify(e.detail.amount), -        callback_url: URI(e.detail.callback_url) +        amount: JSON.stringify(msg.amount), +        callback_url: URI(msg.callback_url)            .absoluteTo(document.location.href),          bank_url: document.location.href, -        wt_types: JSON.stringify(e.detail.wt_types), +        wt_types: JSON.stringify(msg.wt_types),        };        let uri = URI(chrome.extension.getURL("pages/confirm-create-reserve.html")); -      document.location.href = uri.query(params).href(); +      let redirectUrl = uri.query(params).href(); +      window.location.href = redirectUrl;      }); -    addHandler("taler-confirm-reserve", function(e: CustomEvent) { -      console.log("taler-confirm-reserve with " + JSON.stringify(e.detail)); -      let msg = { +    addHandler("taler-confirm-reserve", (msg: any, sendResponse: any) => { +      console.log("taler-confirm-reserve with " + JSON.stringify(msg)); +      let walletMsg = {          type: "confirm-reserve",          detail: { -          reservePub: e.detail.reserve_pub +          reservePub: msg.reserve_pub          }        }; -      chrome.runtime.sendMessage(msg, (resp) => { +      chrome.runtime.sendMessage(walletMsg, (resp) => {          console.log("confirm reserve done"); +        sendResponse();        });      }); -    addHandler("taler-confirm-contract", function(e: CustomEvent) { -      if (!e.detail.contract_wrapper) { +    addHandler("taler-confirm-contract", (msg: any) => { +      if (!msg.contract_wrapper) {          console.error("contract wrapper missing");          return;        } -      const offer = e.detail.contract_wrapper; +      const offer = msg.contract_wrapper;        if (!offer.contract) {          console.error("contract field missing");          return;        } -      const msg = { +      const walletMsg = {          type: "check-repurchase",          detail: {            contract: offer.contract          },        }; -      chrome.runtime.sendMessage(msg, (resp) => { +      chrome.runtime.sendMessage(walletMsg, (resp: any) => {          if (resp.error) {            console.error("wallet backend error", resp);            return; @@ -293,7 +187,7 @@ namespace TalerNotify {              merchantPageUrl: document.location.href,            };            const target = uri.query(params).href(); -          if (e.detail.replace_navigation === true) { +          if (msg.replace_navigation === true) {              document.location.replace(target);            } else {              document.location.href = target; @@ -302,39 +196,34 @@ namespace TalerNotify {        });      }); -    addHandler("taler-payment-failed", (e: CustomEvent) => { -      const msg = { +    addHandler("taler-payment-failed", (msg: any, sendResponse: any) => { +      const walletMsg = {          type: "payment-failed",          detail: {},        }; -      chrome.runtime.sendMessage(msg, (resp) => { -        let evt = new CustomEvent("taler-payment-failed-ok", { -          detail: {} -        }); -        document.dispatchEvent(evt); -      }); +      chrome.runtime.sendMessage(walletMsg, (resp) => { +        sendResponse(); +      })      }); -    // Should be: taler-request-payment, taler-result-payment - -    addHandler("taler-execute-contract", (e: CustomEvent) => { -      console.log("got taler-execute-contract in content page"); -      const msg = { +    addHandler("taler-get-payment", (msg: any, sendResponse: any) => { +      console.log("got taler-get-payment in content page"); +      const walletMsg = {          type: "execute-payment",          detail: { -          H_contract: e.detail.H_contract, +          H_contract: msg.H_contract,          },        }; -      chrome.runtime.sendMessage(msg, (resp) => { +      chrome.runtime.sendMessage(walletMsg, (resp) => {          console.log("got resp");          console.dir(resp);          if (!resp.success) {            console.log("got event detail:"); -          console.dir(e.detail); -          if (e.detail.offering_url) { -            console.log("offering url", e.detail.offering_url); -            window.location.href = e.detail.offering_url; +          console.dir(msg); +          if (msg.offering_url) { +            console.log("offering url", msg.offering_url); +            window.location.href = msg.offering_url;            } else {              console.error("execute-payment failed");            } @@ -348,14 +237,11 @@ namespace TalerNotify {          // We have the details for then payment, the merchant page          // is responsible to give it to the merchant. -        let evt = new CustomEvent("taler-notify-payment", { -          detail: { -            H_contract: e.detail.H_contract, -            contract: resp.contract, -            payment: resp.payReq, -          } -        }); -        document.dispatchEvent(evt); +        sendResponse({ +                       H_contract: msg.H_contract, +                       contract: resp.contract, +                       payment: resp.payReq, +                     });        });      });    } diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts index 367c9cbcd..32b85e7b0 100644 --- a/lib/wallet/wallet.ts +++ b/lib/wallet/wallet.ts @@ -720,6 +720,10 @@ export class Wallet {      return Query(this.db)        .get("reserves", req.reservePub)        .then((r) => { +        if (!r) { +          console.error("Unable to confirm reserve, not found in DB"); +          return; +        }          r.confirmed = true;          return Query(this.db)            .put("reserves", r) @@ -770,6 +774,7 @@ export class Wallet {    }    storeCoin(coin: Coin): Promise<void> { +    console.log("storing coin", new Date());      let historyEntry = {        type: "withdraw",        timestamp: (new Date).getTime(), @@ -813,6 +818,9 @@ export class Wallet {      let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount,                                                   denomsAvailable); +    // Number of coins we try to withdraw at once +    const concurrency = 1; +      let ps = denomsForWithdraw.map((denom) => {        console.log("withdrawing", JSON.stringify(denom));        // Do the withdraw asynchronously, so crypto is interleaved diff --git a/lib/wallet/wxMessaging.ts b/lib/wallet/wxMessaging.ts index 9c08b20ca..514cdce13 100644 --- a/lib/wallet/wxMessaging.ts +++ b/lib/wallet/wxMessaging.ts @@ -193,24 +193,38 @@ function dispatch(handlers: any, req: any, sender: any, sendResponse: any) {          const p = handlers[req.type](req.detail, sender);          return p.then((r: any) => { -          sendResponse(r); +          try { +            sendResponse(r); +          } catch (e) { +            // might fail if tab disconnected +          }          })        })        .catch((e) => {          console.log(`exception during wallet handler for '${req.type}'`);          console.log("request", req);          console.error(e); -        sendResponse({ -                       error: "exception", -                       hint: e.message, -                       stack: e.stack.toString() -                     }); +        try { +          sendResponse({ +                         error: "exception", +                         hint: e.message, +                         stack: e.stack.toString() +                       }); + +        } catch (e) { +          // might fail if tab disconnected +        }        });      // The sendResponse call is async      return true;    } else {      console.error(`Request type ${JSON.stringify(req)} unknown, req ${req.type}`); -    sendResponse({error: "request unknown"}); +    try { +      sendResponse({error: "request unknown"}); +    } catch (e) { +      // might fail if tab disconnected +    } +      // The sendResponse call is sync      return false;    } @@ -300,6 +314,7 @@ export function wxMain() {        let uri = URI(tab.url);        if (uri.protocol() == "http" || uri.protocol() == "https") {          console.log("injecting into existing tab", tab.id); +        chrome.tabs.executeScript(tab.id, {file: "lib/vendor/URI.js"});          chrome.tabs.executeScript(tab.id, {file: "content_scripts/notify.js"});        }      } diff --git a/manifest.json b/manifest.json index d885aebb0..1291bc37a 100644 --- a/manifest.json +++ b/manifest.json @@ -31,7 +31,6 @@        "matches": ["*://*/*"],        "js": [          "lib/vendor/URI.js", -        "lib/vendor/system-csp-production.src.js",          "content_scripts/notify.js"        ],        "run_at": "document_start" | 
