michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu } = Components; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: // JS shim that contains the callback functions to be triggered from the michael@0: // payment provider's code in order to fire DOMRequest events. michael@0: const kPaymentShimFile = "chrome://b2g/content/payment.js"; michael@0: michael@0: // Type of MozChromEvents to handle payment dialogs. michael@0: const kOpenPaymentConfirmationEvent = "open-payment-confirmation-dialog"; michael@0: const kOpenPaymentFlowEvent = "open-payment-flow-dialog"; michael@0: michael@0: const PREF_DEBUG = "dom.payment.debug"; michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", michael@0: "@mozilla.org/uuid-generator;1", michael@0: "nsIUUIDGenerator"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", michael@0: "resource://gre/modules/SystemAppProxy.jsm"); michael@0: michael@0: function PaymentUI() { michael@0: try { michael@0: this._debug = michael@0: Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL michael@0: && Services.prefs.getBoolPref(PREF_DEBUG); michael@0: } catch(e) { michael@0: this._debug = false; michael@0: } michael@0: } michael@0: michael@0: PaymentUI.prototype = { michael@0: michael@0: confirmPaymentRequest: function confirmPaymentRequest(aRequestId, michael@0: aRequests, michael@0: aSuccessCb, michael@0: aErrorCb) { michael@0: let _error = function _error(errorMsg) { michael@0: if (aErrorCb) { michael@0: aErrorCb.onresult(aRequestId, errorMsg); michael@0: } michael@0: }; michael@0: michael@0: // The UI should listen for mozChromeEvent 'open-payment-confirmation-dialog' michael@0: // type in order to create and show the payment request confirmation frame michael@0: // embeded within a trusted dialog. michael@0: let id = kOpenPaymentConfirmationEvent + "-" + this.getRandomId(); michael@0: let detail = { michael@0: type: kOpenPaymentConfirmationEvent, michael@0: id: id, michael@0: requestId: aRequestId, michael@0: paymentRequests: aRequests michael@0: }; michael@0: michael@0: // Once the user confirm the payment request and makes his choice, we get michael@0: // back to the DOM part to get the appropriate payment flow information michael@0: // based on the selected payment provider. michael@0: this._handleSelection = (function _handleSelection(evt) { michael@0: let msg = evt.detail; michael@0: if (msg.id != id) { michael@0: return; michael@0: } michael@0: michael@0: if (msg.userSelection && aSuccessCb) { michael@0: aSuccessCb.onresult(aRequestId, msg.userSelection); michael@0: } else if (msg.errorMsg) { michael@0: _error(msg.errorMsg); michael@0: } michael@0: michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._handleSelection); michael@0: this._handleSelection = null; michael@0: }).bind(this); michael@0: SystemAppProxy.addEventListener("mozContentEvent", this._handleSelection); michael@0: michael@0: SystemAppProxy.dispatchEvent(detail); michael@0: }, michael@0: michael@0: showPaymentFlow: function showPaymentFlow(aRequestId, michael@0: aPaymentFlowInfo, michael@0: aErrorCb) { michael@0: let _error = function _error(errorMsg) { michael@0: if (aErrorCb) { michael@0: aErrorCb.onresult(aRequestId, errorMsg); michael@0: } michael@0: }; michael@0: michael@0: // We ask the UI to browse to the selected payment flow. michael@0: let id = kOpenPaymentFlowEvent + "-" + this.getRandomId(); michael@0: let detail = { michael@0: type: kOpenPaymentFlowEvent, michael@0: id: id, michael@0: requestId: aRequestId, michael@0: uri: aPaymentFlowInfo.uri, michael@0: method: aPaymentFlowInfo.requestMethod, michael@0: jwt: aPaymentFlowInfo.jwt michael@0: }; michael@0: michael@0: // At some point the UI would send the created iframe back so the michael@0: // callbacks for firing DOMRequest events can be loaded on its michael@0: // content. michael@0: this._loadPaymentShim = (function _loadPaymentShim(evt) { michael@0: let msg = evt.detail; michael@0: if (msg.id != id) { michael@0: return; michael@0: } michael@0: michael@0: if (msg.errorMsg) { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); michael@0: this._loadPaymentShim = null; michael@0: _error("ERROR_LOADING_PAYMENT_SHIM: " + msg.errorMsg); michael@0: return; michael@0: } michael@0: michael@0: if (!msg.frame) { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); michael@0: this._loadPaymentShim = null; michael@0: _error("ERROR_LOADING_PAYMENT_SHIM"); michael@0: return; michael@0: } michael@0: michael@0: // Try to load the payment shim file containing the payment callbacks michael@0: // in the content script. michael@0: let frame = msg.frame; michael@0: let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner) michael@0: .frameLoader; michael@0: let mm = frameLoader.messageManager; michael@0: try { michael@0: mm.loadFrameScript(kPaymentShimFile, true, true); michael@0: mm.sendAsyncMessage("Payment:LoadShim", { requestId: aRequestId }); michael@0: } catch (e) { michael@0: if (this._debug) { michael@0: this.LOG("Error loading " + kPaymentShimFile + " as a frame script: " michael@0: + e); michael@0: } michael@0: _error("ERROR_LOADING_PAYMENT_SHIM"); michael@0: } finally { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); michael@0: this._loadPaymentShim = null; michael@0: } michael@0: }).bind(this); michael@0: SystemAppProxy.addEventListener("mozContentEvent", this._loadPaymentShim); michael@0: michael@0: // We also listen for UI notifications about a closed payment flow. The UI michael@0: // should provide the reason of the closure within the 'errorMsg' parameter michael@0: this._notifyPayFlowClosed = (function _notifyPayFlowClosed(evt) { michael@0: let msg = evt.detail; michael@0: if (msg.id != id) { michael@0: return; michael@0: } michael@0: michael@0: if (msg.type != 'cancel') { michael@0: return; michael@0: } michael@0: michael@0: if (msg.errorMsg) { michael@0: _error(msg.errorMsg); michael@0: } michael@0: SystemAppProxy.removeEventListener("mozContentEvent", michael@0: this._notifyPayFlowClosed); michael@0: this._notifyPayFlowClosed = null; michael@0: }).bind(this); michael@0: SystemAppProxy.addEventListener("mozContentEvent", michael@0: this._notifyPayFlowClosed); michael@0: michael@0: SystemAppProxy.dispatchEvent(detail); michael@0: }, michael@0: michael@0: cleanup: function cleanup() { michael@0: if (this._handleSelection) { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._handleSelection); michael@0: this._handleSelection = null; michael@0: } michael@0: michael@0: if (this._notifyPayFlowClosed) { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._notifyPayFlowClosed); michael@0: this._notifyPayFlowClosed = null; michael@0: } michael@0: michael@0: if (this._loadPaymentShim) { michael@0: SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); michael@0: this._loadPaymentShim = null; michael@0: } michael@0: }, michael@0: michael@0: getRandomId: function getRandomId() { michael@0: return uuidgen.generateUUID().toString(); michael@0: }, michael@0: michael@0: LOG: function LOG(s) { michael@0: if (!this._debug) { michael@0: return; michael@0: } michael@0: dump("-*- PaymentGlue: " + s + "\n"); michael@0: }, michael@0: michael@0: classID: Components.ID("{8b83eabc-7929-47f4-8b48-4dea8d887e4b}"), michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]) michael@0: } michael@0: michael@0: this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]);