|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const { classes: Cc, interfaces: Ci, utils: Cu } = Components; |
|
8 |
|
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
10 Cu.import("resource://gre/modules/Services.jsm"); |
|
11 |
|
12 // JS shim that contains the callback functions to be triggered from the |
|
13 // payment provider's code in order to fire DOMRequest events. |
|
14 const kPaymentShimFile = "chrome://b2g/content/payment.js"; |
|
15 |
|
16 // Type of MozChromEvents to handle payment dialogs. |
|
17 const kOpenPaymentConfirmationEvent = "open-payment-confirmation-dialog"; |
|
18 const kOpenPaymentFlowEvent = "open-payment-flow-dialog"; |
|
19 |
|
20 const PREF_DEBUG = "dom.payment.debug"; |
|
21 |
|
22 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen", |
|
23 "@mozilla.org/uuid-generator;1", |
|
24 "nsIUUIDGenerator"); |
|
25 |
|
26 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", |
|
27 "resource://gre/modules/SystemAppProxy.jsm"); |
|
28 |
|
29 function PaymentUI() { |
|
30 try { |
|
31 this._debug = |
|
32 Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL |
|
33 && Services.prefs.getBoolPref(PREF_DEBUG); |
|
34 } catch(e) { |
|
35 this._debug = false; |
|
36 } |
|
37 } |
|
38 |
|
39 PaymentUI.prototype = { |
|
40 |
|
41 confirmPaymentRequest: function confirmPaymentRequest(aRequestId, |
|
42 aRequests, |
|
43 aSuccessCb, |
|
44 aErrorCb) { |
|
45 let _error = function _error(errorMsg) { |
|
46 if (aErrorCb) { |
|
47 aErrorCb.onresult(aRequestId, errorMsg); |
|
48 } |
|
49 }; |
|
50 |
|
51 // The UI should listen for mozChromeEvent 'open-payment-confirmation-dialog' |
|
52 // type in order to create and show the payment request confirmation frame |
|
53 // embeded within a trusted dialog. |
|
54 let id = kOpenPaymentConfirmationEvent + "-" + this.getRandomId(); |
|
55 let detail = { |
|
56 type: kOpenPaymentConfirmationEvent, |
|
57 id: id, |
|
58 requestId: aRequestId, |
|
59 paymentRequests: aRequests |
|
60 }; |
|
61 |
|
62 // Once the user confirm the payment request and makes his choice, we get |
|
63 // back to the DOM part to get the appropriate payment flow information |
|
64 // based on the selected payment provider. |
|
65 this._handleSelection = (function _handleSelection(evt) { |
|
66 let msg = evt.detail; |
|
67 if (msg.id != id) { |
|
68 return; |
|
69 } |
|
70 |
|
71 if (msg.userSelection && aSuccessCb) { |
|
72 aSuccessCb.onresult(aRequestId, msg.userSelection); |
|
73 } else if (msg.errorMsg) { |
|
74 _error(msg.errorMsg); |
|
75 } |
|
76 |
|
77 SystemAppProxy.removeEventListener("mozContentEvent", this._handleSelection); |
|
78 this._handleSelection = null; |
|
79 }).bind(this); |
|
80 SystemAppProxy.addEventListener("mozContentEvent", this._handleSelection); |
|
81 |
|
82 SystemAppProxy.dispatchEvent(detail); |
|
83 }, |
|
84 |
|
85 showPaymentFlow: function showPaymentFlow(aRequestId, |
|
86 aPaymentFlowInfo, |
|
87 aErrorCb) { |
|
88 let _error = function _error(errorMsg) { |
|
89 if (aErrorCb) { |
|
90 aErrorCb.onresult(aRequestId, errorMsg); |
|
91 } |
|
92 }; |
|
93 |
|
94 // We ask the UI to browse to the selected payment flow. |
|
95 let id = kOpenPaymentFlowEvent + "-" + this.getRandomId(); |
|
96 let detail = { |
|
97 type: kOpenPaymentFlowEvent, |
|
98 id: id, |
|
99 requestId: aRequestId, |
|
100 uri: aPaymentFlowInfo.uri, |
|
101 method: aPaymentFlowInfo.requestMethod, |
|
102 jwt: aPaymentFlowInfo.jwt |
|
103 }; |
|
104 |
|
105 // At some point the UI would send the created iframe back so the |
|
106 // callbacks for firing DOMRequest events can be loaded on its |
|
107 // content. |
|
108 this._loadPaymentShim = (function _loadPaymentShim(evt) { |
|
109 let msg = evt.detail; |
|
110 if (msg.id != id) { |
|
111 return; |
|
112 } |
|
113 |
|
114 if (msg.errorMsg) { |
|
115 SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); |
|
116 this._loadPaymentShim = null; |
|
117 _error("ERROR_LOADING_PAYMENT_SHIM: " + msg.errorMsg); |
|
118 return; |
|
119 } |
|
120 |
|
121 if (!msg.frame) { |
|
122 SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); |
|
123 this._loadPaymentShim = null; |
|
124 _error("ERROR_LOADING_PAYMENT_SHIM"); |
|
125 return; |
|
126 } |
|
127 |
|
128 // Try to load the payment shim file containing the payment callbacks |
|
129 // in the content script. |
|
130 let frame = msg.frame; |
|
131 let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner) |
|
132 .frameLoader; |
|
133 let mm = frameLoader.messageManager; |
|
134 try { |
|
135 mm.loadFrameScript(kPaymentShimFile, true, true); |
|
136 mm.sendAsyncMessage("Payment:LoadShim", { requestId: aRequestId }); |
|
137 } catch (e) { |
|
138 if (this._debug) { |
|
139 this.LOG("Error loading " + kPaymentShimFile + " as a frame script: " |
|
140 + e); |
|
141 } |
|
142 _error("ERROR_LOADING_PAYMENT_SHIM"); |
|
143 } finally { |
|
144 SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); |
|
145 this._loadPaymentShim = null; |
|
146 } |
|
147 }).bind(this); |
|
148 SystemAppProxy.addEventListener("mozContentEvent", this._loadPaymentShim); |
|
149 |
|
150 // We also listen for UI notifications about a closed payment flow. The UI |
|
151 // should provide the reason of the closure within the 'errorMsg' parameter |
|
152 this._notifyPayFlowClosed = (function _notifyPayFlowClosed(evt) { |
|
153 let msg = evt.detail; |
|
154 if (msg.id != id) { |
|
155 return; |
|
156 } |
|
157 |
|
158 if (msg.type != 'cancel') { |
|
159 return; |
|
160 } |
|
161 |
|
162 if (msg.errorMsg) { |
|
163 _error(msg.errorMsg); |
|
164 } |
|
165 SystemAppProxy.removeEventListener("mozContentEvent", |
|
166 this._notifyPayFlowClosed); |
|
167 this._notifyPayFlowClosed = null; |
|
168 }).bind(this); |
|
169 SystemAppProxy.addEventListener("mozContentEvent", |
|
170 this._notifyPayFlowClosed); |
|
171 |
|
172 SystemAppProxy.dispatchEvent(detail); |
|
173 }, |
|
174 |
|
175 cleanup: function cleanup() { |
|
176 if (this._handleSelection) { |
|
177 SystemAppProxy.removeEventListener("mozContentEvent", this._handleSelection); |
|
178 this._handleSelection = null; |
|
179 } |
|
180 |
|
181 if (this._notifyPayFlowClosed) { |
|
182 SystemAppProxy.removeEventListener("mozContentEvent", this._notifyPayFlowClosed); |
|
183 this._notifyPayFlowClosed = null; |
|
184 } |
|
185 |
|
186 if (this._loadPaymentShim) { |
|
187 SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim); |
|
188 this._loadPaymentShim = null; |
|
189 } |
|
190 }, |
|
191 |
|
192 getRandomId: function getRandomId() { |
|
193 return uuidgen.generateUUID().toString(); |
|
194 }, |
|
195 |
|
196 LOG: function LOG(s) { |
|
197 if (!this._debug) { |
|
198 return; |
|
199 } |
|
200 dump("-*- PaymentGlue: " + s + "\n"); |
|
201 }, |
|
202 |
|
203 classID: Components.ID("{8b83eabc-7929-47f4-8b48-4dea8d887e4b}"), |
|
204 |
|
205 QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]) |
|
206 } |
|
207 |
|
208 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]); |