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