Bumping manifests a=b2g-bump
[gecko.git] / dom / payment / PaymentProvider.js
blob4fe187b3c193192fbf2c5230f5bcee54163f019f
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/. */
5 "use strict";
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
10 Cu.import("resource://gre/modules/Services.jsm");
12 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
13                                    "@mozilla.org/childprocessmessagemanager;1",
14                                    "nsIMessageSender");
16 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
17                                    "@mozilla.org/uuid-generator;1",
18                                    "nsIUUIDGenerator");
21 const PREF_DEBUG = "dom.payment.debug";
23 let _debug;
24 try {
25   _debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
26            && Services.prefs.getBoolPref(PREF_DEBUG);
27 } catch(e) {
28   _debug = false;
31 function DEBUG(s) {
32   if (!_debug) {
33     return;
34   }
35   dump("== Payment Provider == " + s + "\n");
38 function DEBUG_E(s) {
39   dump("== Payment Provider ERROR == " + s + "\n");
42 const kPaymentFlowCancelled = "payment-flow-cancelled";
44 function PaymentProvider() {
47 PaymentProvider.prototype = {
48   init: function(aWindow) {
49     let docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
50                           .getInterface(Ci.nsIWebNavigation)
51                           .QueryInterface(Ci.nsIDocShell);
52     this._requestId = docshell.paymentRequestId;
53     this._oncancelObserver = this.oncancel.bind(this);
54     Services.obs.addObserver(this._oncancelObserver,
55                              kPaymentFlowCancelled,
56                              false);
57     this._strategy = Cc["@mozilla.org/payment/provider-strategy;1"]
58                        .createInstance(Ci.nsIPaymentProviderStrategy);
59     this._window = aWindow;
60   },
62   paymentSuccess: function(aResult) {
63     _debug && DEBUG("paymentSuccess " + aResult);
64     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
65                  .createInstance(Ci.nsIPaymentUIGlue);
66     glue.closePaymentFlow(this._requestId).then(() => {
67       if (!this._requestId) {
68         return;
69       }
70       cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
71                                                  requestId: this._requestId });
72     });
73   },
75   paymentFailed: function(aError) {
76     _debug && DEBUG("paymentFailed " + aError);
77     let glue = Cc["@mozilla.org/payment/ui-glue;1"]
78                  .createInstance(Ci.nsIPaymentUIGlue);
79     glue.closePaymentFlow(this._requestId).then(() => {
80       if (!this._requestId) {
81         return;
82       }
83       cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aError,
84                                                 requestId: this._requestId });
85     });
87   },
89   get paymentServiceId() {
90     return this._strategy.paymentServiceId;
91   },
93   set paymentServiceId(aServiceId) {
94     this._strategy.paymentServiceId = aServiceId;
95   },
97   /**
98    * We expose to the payment provider the information of all the SIMs
99    * available in the device. iccInfo is an object of this form:
100    * {
101    *   "serviceId1": {
102    *      mcc: <string>,
103    *      mnc: <string>,
104    *      iccId: <string>,
105    *      dataPrimary: <boolean>
106    *    },
107    *   "serviceIdN": {...}
108    * }
109    */
110   get iccInfo() {
111     return this._strategy.iccInfo;
112   },
114   oncancel: function() {
115     _debug && DEBUG("Cleaning up!");
117     this._strategy.cleanup();
118     Services.obs.removeObserver(this._oncancelObserver, kPaymentFlowCancelled);
119     if (this._cleanup) {
120       this._cleanup();
121     }
122   },
124   classID: Components.ID("{82144756-72ab-45b7-8621-f3dad431dd2f}"),
126   contractID: "@mozilla.org/payment/provider;1",
128   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
129                                          Ci.nsIObserver,
130                                          Ci.nsIDOMGlobalPropertyInitializer])
133 #if defined(MOZ_B2G_RIL) || defined(MOZ_WIDGET_ANDROID)
135 XPCOMUtils.defineLazyServiceGetter(this, "smsService",
136                                    "@mozilla.org/sms/smsservice;1",
137                                    "nsISmsService");
139 const kSilentSmsReceivedTopic = "silent-sms-received";
141 // In order to send messages through nsISmsService, we need to implement
142 // nsIMobileMessageCallback, as the WebSMS API implementation is not usable
143 // from JS.
144 function SilentSmsRequest(aDOMRequest) {
145   this.request = aDOMRequest;
148 SilentSmsRequest.prototype = {
150   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
152   classID: Components.ID("{1d58889c-5660-4cca-a8fd-97ef63e5d3b2}"),
154   notifyMessageSent: function notifyMessageSent(aMessage) {
155     _debug && DEBUG("Silent message successfully sent");
156     Services.DOMRequest.fireSuccessAsync(this.request, aMessage);
157   },
159   notifySendMessageFailed: function notifySendMessageFailed(aError) {
160     DEBUG_E("Error sending silent message " + aError);
161     Services.DOMRequest.fireErrorAsync(this.request, aError);
162   }
165 PaymentProvider.prototype._silentNumbers = null;
167 PaymentProvider.prototype._silentSmsObservers = null;
169 PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
170   _debug && DEBUG("Sending silent message " + aNumber + " - " + aMessage);
172   let request = Services.DOMRequest.createRequest(this._window);
174   if (this._strategy.paymentServiceId === null) {
175     DEBUG_E("No payment service ID set. Cannot send silent SMS");
176     Services.DOMRequest.fireErrorAsync(request,
177                                        "NO_PAYMENT_SERVICE_ID");
178     return request;
179   }
181   let smsRequest = new SilentSmsRequest(request);
182   smsService.send(this._strategy.paymentServiceId, aNumber, aMessage, true,
183                   smsRequest);
184   return request;
187 PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
188   _debug && DEBUG("observeSilentSms " + aNumber);
190   if (!this._silentSmsObservers) {
191     this._silentSmsObservers = {};
192     this._silentNumbers = [];
193     this._onSilentSmsObserver = this._onSilentSms.bind(this);
194     Services.obs.addObserver(this._onSilentSmsObserver,
195                              kSilentSmsReceivedTopic,
196                              false);
197   }
199   if (!this._silentSmsObservers[aNumber]) {
200     this._silentSmsObservers[aNumber] = [];
201     this._silentNumbers.push(aNumber);
202     smsService.addSilentNumber(aNumber);
203   }
205   if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
206     this._silentSmsObservers[aNumber].push(aCallback);
207   }
208   return;
211 PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
212   _debug && DEBUG("removeSilentSmsObserver " + aNumber);
214   if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
215     _debug && DEBUG("No observers for " + aNumber);
216     return;
217   }
219   let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
220   if (index != -1) {
221     this._silentSmsObservers[aNumber].splice(index, 1);
222     if (this._silentSmsObservers[aNumber].length == 0) {
223       this._silentSmsObservers[aNumber] = null;
224       this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
225       smsService.removeSilentNumber(aNumber);
226     }
227   } else if (_debug) {
228     DEBUG("No callback found for " + aNumber);
229   }
230   return;
233 PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) {
234   _debug && DEBUG("Got silent message! " + aSubject.sender + " - " + aSubject.body);
236   let number = aSubject.sender;
237   if (!number || this._silentNumbers.indexOf(number) == -1) {
238     _debug && DEBUG("No observers for " + number);
239     return;
240   }
242   // If the service ID is null it means that the payment provider asked the
243   // user for her MSISDN, so we are in a MT only SMS auth flow. In this case
244   // we manually set the service ID to the one corresponding with the SIM
245   // that received the SMS.
246   if (this._strategy.paymentServiceId === null) {
247     let i = 0;
248     while(i < gRil.numRadioInterfaces) {
249       if (this.iccInfo[i].iccId === aSubject.iccId) {
250         this._strategy.paymentServiceId = i;
251         break;
252       }
253       i++;
254     }
255   }
257   this._silentSmsObservers[number].forEach(function(callback) {
258     callback(aSubject);
259   });
262 PaymentProvider.prototype._cleanup = function() {
263   if (!this._silentNumbers) {
264     return;
265   }
267   while (this._silentNumbers.length) {
268     let number = this._silentNumbers.pop();
269     smsService.removeSilentNumber(number);
270   }
271   this._silentNumbers = null;
272   this._silentSmsObservers = null;
273   Services.obs.removeObserver(this._onSilentSmsObserver,
274                               kSilentSmsReceivedTopic);
277 #else
279 PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
280   throw Cr.NS_ERROR_NOT_IMPLEMENTED;
283 PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
284   throw Cr.NS_ERROR_NOT_IMPLEMENTED;
287 PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
288   throw Cr.NS_ERROR_NOT_IMPLEMENTED;
291 #endif
293 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentProvider]);