зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1097928 - Convert MozPaymentProvider to WebIDL. r=bholley,fabrice
This commit is contained in:
Родитель
a8111199f6
Коммит
d48b684909
|
@ -1,484 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* 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/. */
|
||||
|
||||
// This JS shim contains the callbacks to fire DOMRequest events for
|
||||
// navigator.pay API within the payment processor's scope.
|
||||
|
||||
"use strict";
|
||||
|
||||
let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PREF_DEBUG = "dom.payment.debug";
|
||||
|
||||
let _debug;
|
||||
try {
|
||||
_debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
|
||||
&& Services.prefs.getBoolPref(PREF_DEBUG);
|
||||
} catch(e){
|
||||
_debug = false;
|
||||
}
|
||||
|
||||
function LOG(s) {
|
||||
if (!_debug) {
|
||||
return;
|
||||
}
|
||||
dump("== Payment flow == " + s + "\n");
|
||||
}
|
||||
|
||||
function LOGE(s) {
|
||||
dump("== Payment flow ERROR == " + s + "\n");
|
||||
}
|
||||
|
||||
if (_debug) {
|
||||
LOG("Frame script injected");
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
|
||||
"resource://gre/modules/SystemAppProxy.jsm");
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
|
||||
"@mozilla.org/ril/content-helper;1",
|
||||
"nsIIccProvider");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "smsService",
|
||||
"@mozilla.org/sms/smsservice;1",
|
||||
"nsISmsService");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
||||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
const kSilentSmsReceivedTopic = "silent-sms-received";
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
|
||||
const kRilDefaultDataServiceId = "ril.data.defaultServiceId";
|
||||
const kRilDefaultPaymentServiceId = "ril.payment.defaultServiceId";
|
||||
|
||||
const MOBILEMESSAGECALLBACK_CID =
|
||||
Components.ID("{b484d8c9-6be4-4f94-ab60-c9c7ebcc853d}");
|
||||
|
||||
// In order to send messages through nsISmsService, we need to implement
|
||||
// nsIMobileMessageCallback, as the WebSMS API implementation is not usable
|
||||
// from JS.
|
||||
function SilentSmsRequest() {
|
||||
}
|
||||
|
||||
SilentSmsRequest.prototype = {
|
||||
__exposedProps__: {
|
||||
onsuccess: "rw",
|
||||
onerror: "rw"
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
|
||||
|
||||
classID: MOBILEMESSAGECALLBACK_CID,
|
||||
|
||||
set onsuccess(aSuccessCallback) {
|
||||
this._onsuccess = aSuccessCallback;
|
||||
},
|
||||
|
||||
set onerror(aErrorCallback) {
|
||||
this._onerror = aErrorCallback;
|
||||
},
|
||||
|
||||
notifyMessageSent: function notifyMessageSent(aMessage) {
|
||||
if (_debug) {
|
||||
LOG("Silent message successfully sent");
|
||||
}
|
||||
this._onsuccess(aMessage);
|
||||
},
|
||||
|
||||
notifySendMessageFailed: function notifySendMessageFailed(aError) {
|
||||
LOGE("Error sending silent message " + aError);
|
||||
this._onerror(aError);
|
||||
}
|
||||
};
|
||||
|
||||
function PaymentSettings() {
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
|
||||
[kRilDefaultDataServiceId, kRilDefaultPaymentServiceId].forEach(setting => {
|
||||
gSettingsService.createLock().get(setting, this);
|
||||
});
|
||||
}
|
||||
|
||||
PaymentSettings.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISettingsServiceCallback,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
dataServiceId: 0,
|
||||
_paymentServiceId: 0,
|
||||
|
||||
get paymentServiceId() {
|
||||
return this._paymentServiceId;
|
||||
},
|
||||
|
||||
set paymentServiceId(serviceId) {
|
||||
// We allow the payment provider to set the service ID that will be used
|
||||
// for the payment process.
|
||||
// This service ID will be the one used by the silent SMS flow.
|
||||
// If the payment is done with an external SIM, the service ID must be set
|
||||
// to null.
|
||||
if (serviceId != null && serviceId >= gRil.numRadioInterfaces) {
|
||||
LOGE("Invalid service ID " + serviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
gSettingsService.createLock().set(kRilDefaultPaymentServiceId,
|
||||
serviceId, null);
|
||||
this._paymentServiceId = serviceId;
|
||||
},
|
||||
|
||||
setServiceId: function(aName, aValue) {
|
||||
switch (aName) {
|
||||
case kRilDefaultDataServiceId:
|
||||
this.dataServiceId = aValue;
|
||||
if (_debug) {
|
||||
LOG("dataServiceId " + this.dataServiceId);
|
||||
}
|
||||
break;
|
||||
case kRilDefaultPaymentServiceId:
|
||||
this._paymentServiceId = aValue;
|
||||
if (_debug) {
|
||||
LOG("paymentServiceId " + this._paymentServiceId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handle: function(aName, aValue) {
|
||||
if (aName != kRilDefaultDataServiceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setServiceId(aName, aValue);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != kMozSettingsChangedObserverTopic) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ('wrappedJSObject' in aSubject) {
|
||||
aSubject = aSubject.wrappedJSObject;
|
||||
}
|
||||
if (!aSubject.key ||
|
||||
(aSubject.key !== kRilDefaultDataServiceId &&
|
||||
aSubject.key !== kRilDefaultPaymentServiceId)) {
|
||||
return;
|
||||
}
|
||||
this.setServiceId(aSubject.key, aSubject.value);
|
||||
} catch (e) {
|
||||
LOGE(e);
|
||||
}
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
const kClosePaymentFlowEvent = "close-payment-flow-dialog";
|
||||
|
||||
let gRequestId;
|
||||
|
||||
let PaymentProvider = {
|
||||
#ifdef MOZ_B2G_RIL
|
||||
__exposedProps__: {
|
||||
paymentSuccess: "r",
|
||||
paymentFailed: "r",
|
||||
paymentServiceId: "rw",
|
||||
iccInfo: "r",
|
||||
sendSilentSms: "r",
|
||||
observeSilentSms: "r",
|
||||
removeSilentSmsObserver: "r"
|
||||
},
|
||||
#else
|
||||
__exposedProps__: {
|
||||
paymentSuccess: "r",
|
||||
paymentFailed: "r"
|
||||
},
|
||||
#endif
|
||||
|
||||
_init: function _init() {
|
||||
#ifdef MOZ_B2G_RIL
|
||||
this._settings = new PaymentSettings();
|
||||
#endif
|
||||
},
|
||||
|
||||
_closePaymentFlowDialog: function _closePaymentFlowDialog(aCallback) {
|
||||
// After receiving the payment provider confirmation about the
|
||||
// successful or failed payment flow, we notify the UI to close the
|
||||
// payment flow dialog and return to the caller application.
|
||||
let id = kClosePaymentFlowEvent + "-" + uuidgen.generateUUID().toString();
|
||||
|
||||
let detail = {
|
||||
type: kClosePaymentFlowEvent,
|
||||
id: id,
|
||||
requestId: gRequestId
|
||||
};
|
||||
|
||||
// In order to avoid race conditions, we wait for the UI to notify that
|
||||
// it has successfully closed the payment flow and has recovered the
|
||||
// caller app, before notifying the parent process to fire the success
|
||||
// or error event over the DOMRequest.
|
||||
SystemAppProxy.addEventListener("mozContentEvent",
|
||||
function closePaymentFlowReturn(evt) {
|
||||
if (evt.detail.id == id && aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
|
||||
SystemAppProxy.removeEventListener("mozContentEvent",
|
||||
closePaymentFlowReturn);
|
||||
|
||||
let glue = Cc["@mozilla.org/payment/ui-glue;1"]
|
||||
.createInstance(Ci.nsIPaymentUIGlue);
|
||||
glue.cleanup();
|
||||
});
|
||||
|
||||
SystemAppProxy.dispatchEvent(detail);
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
this._cleanUp();
|
||||
#endif
|
||||
},
|
||||
|
||||
paymentSuccess: function paymentSuccess(aResult) {
|
||||
if (_debug) {
|
||||
LOG("paymentSuccess " + aResult);
|
||||
}
|
||||
|
||||
PaymentProvider._closePaymentFlowDialog(function notifySuccess() {
|
||||
if (!gRequestId) {
|
||||
return;
|
||||
}
|
||||
cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
|
||||
requestId: gRequestId });
|
||||
});
|
||||
},
|
||||
|
||||
paymentFailed: function paymentFailed(aErrorMsg) {
|
||||
LOGE("paymentFailed " + aErrorMsg);
|
||||
|
||||
PaymentProvider._closePaymentFlowDialog(function notifyError() {
|
||||
if (!gRequestId) {
|
||||
return;
|
||||
}
|
||||
cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aErrorMsg,
|
||||
requestId: gRequestId });
|
||||
});
|
||||
},
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
get paymentServiceId() {
|
||||
return this._settings.paymentServiceId;
|
||||
},
|
||||
|
||||
set paymentServiceId(serviceId) {
|
||||
this._settings.paymentServiceId = serviceId;
|
||||
},
|
||||
|
||||
// We expose to the payment provider the information of all the SIMs
|
||||
// available in the device. iccInfo is an object of this form:
|
||||
// {
|
||||
// "serviceId1": {
|
||||
// mcc: <string>,
|
||||
// mnc: <string>,
|
||||
// iccId: <string>,
|
||||
// dataPrimary: <boolean>
|
||||
// },
|
||||
// "serviceIdN": {...}
|
||||
// }
|
||||
get iccInfo() {
|
||||
if (!this._iccInfo) {
|
||||
this._iccInfo = {};
|
||||
for (let i = 0; i < gRil.numRadioInterfaces; i++) {
|
||||
let info = iccProvider.getIccInfo(i);
|
||||
if (!info) {
|
||||
LOGE("Tried to get the ICC info for an invalid service ID " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
this._iccInfo[i] = {
|
||||
iccId: info.iccid,
|
||||
mcc: info.mcc,
|
||||
mnc: info.mnc,
|
||||
dataPrimary: i == this._settings.dataServiceId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return Cu.cloneInto(this._iccInfo, content);
|
||||
},
|
||||
|
||||
_silentNumbers: null,
|
||||
_silentSmsObservers: null,
|
||||
|
||||
sendSilentSms: function sendSilentSms(aNumber, aMessage) {
|
||||
if (_debug) {
|
||||
LOG("Sending silent message " + aNumber + " - " + aMessage);
|
||||
}
|
||||
|
||||
let request = new SilentSmsRequest();
|
||||
|
||||
if (this._settings.paymentServiceId === null) {
|
||||
LOGE("No payment service ID set. Cannot send silent SMS");
|
||||
let runnable = {
|
||||
run: function run() {
|
||||
request.notifySendMessageFailed("NO_PAYMENT_SERVICE_ID");
|
||||
}
|
||||
};
|
||||
Services.tm.currentThread.dispatch(runnable,
|
||||
Ci.nsIThread.DISPATCH_NORMAL);
|
||||
return request;
|
||||
}
|
||||
|
||||
smsService.send(this._settings.paymentServiceId, aNumber, aMessage, true,
|
||||
request);
|
||||
return request;
|
||||
},
|
||||
|
||||
observeSilentSms: function observeSilentSms(aNumber, aCallback) {
|
||||
if (_debug) {
|
||||
LOG("observeSilentSms " + aNumber);
|
||||
}
|
||||
|
||||
if (!this._silentSmsObservers) {
|
||||
this._silentSmsObservers = {};
|
||||
this._silentNumbers = [];
|
||||
Services.obs.addObserver(this._onSilentSms.bind(this),
|
||||
kSilentSmsReceivedTopic,
|
||||
false);
|
||||
}
|
||||
|
||||
if (!this._silentSmsObservers[aNumber]) {
|
||||
this._silentSmsObservers[aNumber] = [];
|
||||
this._silentNumbers.push(aNumber);
|
||||
smsService.addSilentNumber(aNumber);
|
||||
}
|
||||
|
||||
if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
|
||||
this._silentSmsObservers[aNumber].push(aCallback);
|
||||
}
|
||||
},
|
||||
|
||||
removeSilentSmsObserver: function removeSilentSmsObserver(aNumber, aCallback) {
|
||||
if (_debug) {
|
||||
LOG("removeSilentSmsObserver " + aNumber);
|
||||
}
|
||||
|
||||
if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
|
||||
if (_debug) {
|
||||
LOG("No observers for " + aNumber);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
|
||||
if (index != -1) {
|
||||
this._silentSmsObservers[aNumber].splice(index, 1);
|
||||
if (this._silentSmsObservers[aNumber].length == 0) {
|
||||
this._silentSmsObservers[aNumber] = null;
|
||||
this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
|
||||
smsService.removeSilentNumber(aNumber);
|
||||
}
|
||||
} else if (_debug) {
|
||||
LOG("No callback found for " + aNumber);
|
||||
}
|
||||
},
|
||||
|
||||
_onSilentSms: function _onSilentSms(aSubject, aTopic, aData) {
|
||||
if (_debug) {
|
||||
LOG("Got silent message! " + aSubject.sender + " - " + aSubject.body);
|
||||
}
|
||||
|
||||
let number = aSubject.sender;
|
||||
if (!number || this._silentNumbers.indexOf(number) == -1) {
|
||||
if (_debug) {
|
||||
LOG("No observers for " + number);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the service ID is null it means that the payment provider asked the
|
||||
// user for her MSISDN, so we are in a MT only SMS auth flow. In this case
|
||||
// we manually set the service ID to the one corresponding with the SIM
|
||||
// that received the SMS.
|
||||
if (this._settings.paymentServiceId === null) {
|
||||
let i = 0;
|
||||
while(i < gRil.numRadioInterfaces) {
|
||||
if (this.iccInfo[i].iccId === aSubject.iccId) {
|
||||
this._settings.paymentServiceId = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this._silentSmsObservers[number].forEach(function(callback) {
|
||||
callback(aSubject);
|
||||
});
|
||||
},
|
||||
|
||||
_cleanUp: function _cleanUp() {
|
||||
if (_debug) {
|
||||
LOG("Cleaning up!");
|
||||
}
|
||||
|
||||
if (!this._silentNumbers) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (this._silentNumbers.length) {
|
||||
let number = this._silentNumbers.pop();
|
||||
smsService.removeSilentNumber(number);
|
||||
}
|
||||
this._silentNumbers = null;
|
||||
this._silentSmsObservers = null;
|
||||
this._settings.cleanup();
|
||||
Services.obs.removeObserver(this._onSilentSms, kSilentSmsReceivedTopic);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// We save the identifier of the DOM request, so we can dispatch the results
|
||||
// of the payment flow to the appropriate content process.
|
||||
addMessageListener("Payment:LoadShim", function receiveMessage(aMessage) {
|
||||
gRequestId = aMessage.json.requestId;
|
||||
PaymentProvider._init();
|
||||
});
|
||||
|
||||
addEventListener("DOMWindowCreated", function(e) {
|
||||
content.wrappedJSObject.mozPaymentProvider = PaymentProvider;
|
||||
});
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
// If the trusted dialog is not closed via paymentSuccess or paymentFailed
|
||||
// a mozContentEvent with type 'cancel' is sent from the UI. We need to listen
|
||||
// for this event to clean up the silent sms observers if any exists.
|
||||
SystemAppProxy.addEventListener("mozContentEvent", function(e) {
|
||||
if (e.detail.type === "cancel") {
|
||||
PaymentProvider._cleanUp();
|
||||
}
|
||||
});
|
||||
#endif
|
|
@ -32,7 +32,6 @@ chrome.jar:
|
|||
* content/content.css (content/content.css)
|
||||
content/touchcontrols.css (content/touchcontrols.css)
|
||||
|
||||
* content/payment.js (content/payment.js)
|
||||
content/identity.js (content/identity.js)
|
||||
|
||||
% override chrome://global/skin/media/videocontrols.css chrome://b2g/content/touchcontrols.css
|
||||
|
|
|
@ -42,9 +42,11 @@ component {a6b2ab13-9037-423a-9897-dde1081be323} OMAContentHandler.js
|
|||
contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.oma.drm.message {a6b2ab13-9037-423a-9897-dde1081be323}
|
||||
contract @mozilla.org/uriloader/content-handler;1?type=application/vnd.oma.dd+xml {a6b2ab13-9037-423a-9897-dde1081be323}
|
||||
|
||||
# PaymentGlue.js
|
||||
# Payments
|
||||
component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
|
||||
contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
|
||||
component {4834b2e1-2c91-44ea-b020-e2581ed279a4} PaymentProviderStrategy.js
|
||||
contract @mozilla.org/payment/provider-strategy;1 {4834b2e1-2c91-44ea-b020-e2581ed279a4}
|
||||
|
||||
# TelProtocolHandler.js
|
||||
component {782775dd-7351-45ea-aff1-0ffa872cfdd2} TelProtocolHandler.js
|
||||
|
|
|
@ -8,14 +8,15 @@ 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";
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
// Type of MozChromEvents to handle payment dialogs.
|
||||
const kOpenPaymentConfirmationEvent = "open-payment-confirmation-dialog";
|
||||
const kOpenPaymentFlowEvent = "open-payment-flow-dialog";
|
||||
const kClosePaymentFlowEvent = "close-payment-flow-dialog";
|
||||
|
||||
// Observer notification topic for payment flow cancelation.
|
||||
const kPaymentFlowCancelled = "payment-flow-cancelled";
|
||||
|
||||
const PREF_DEBUG = "dom.payment.debug";
|
||||
|
||||
|
@ -85,7 +86,7 @@ PaymentUI.prototype = {
|
|||
showPaymentFlow: function showPaymentFlow(aRequestId,
|
||||
aPaymentFlowInfo,
|
||||
aErrorCb) {
|
||||
let _error = function _error(errorMsg) {
|
||||
let _error = (errorMsg) => {
|
||||
if (aErrorCb) {
|
||||
aErrorCb.onresult(aRequestId, errorMsg);
|
||||
}
|
||||
|
@ -96,60 +97,31 @@ PaymentUI.prototype = {
|
|||
let detail = {
|
||||
type: kOpenPaymentFlowEvent,
|
||||
id: id,
|
||||
requestId: aRequestId,
|
||||
uri: aPaymentFlowInfo.uri,
|
||||
method: aPaymentFlowInfo.requestMethod,
|
||||
jwt: aPaymentFlowInfo.jwt
|
||||
requestId: aRequestId
|
||||
};
|
||||
|
||||
// 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) {
|
||||
this._setPaymentRequest = (event) => {
|
||||
let message = event.detail;
|
||||
if (message.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.errorMsg) {
|
||||
SystemAppProxy.removeEventListener("mozContentEvent", this._loadPaymentShim);
|
||||
this._loadPaymentShim = null;
|
||||
_error("ERROR_LOADING_PAYMENT_SHIM: " + msg.errorMsg);
|
||||
return;
|
||||
}
|
||||
let frame = message.frame;
|
||||
let docshell = frame.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
docshell.paymentRequestId = aRequestId;
|
||||
frame.src = aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt;
|
||||
SystemAppProxy.removeEventListener("mozContentEvent",
|
||||
this._setPaymentRequest);
|
||||
};
|
||||
SystemAppProxy.addEventListener("mozContentEvent",
|
||||
this._setPaymentRequest);
|
||||
|
||||
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
|
||||
// We 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) {
|
||||
this._notifyPayFlowClosed = (evt) => {
|
||||
let msg = evt.detail;
|
||||
if (msg.id != id) {
|
||||
return;
|
||||
|
@ -162,44 +134,66 @@ PaymentUI.prototype = {
|
|||
if (msg.errorMsg) {
|
||||
_error(msg.errorMsg);
|
||||
}
|
||||
|
||||
SystemAppProxy.removeEventListener("mozContentEvent",
|
||||
this._notifyPayFlowClosed);
|
||||
this._notifyPayFlowClosed = null;
|
||||
}).bind(this);
|
||||
|
||||
Services.obs.notifyObservers(null, kPaymentFlowCancelled, null);
|
||||
};
|
||||
SystemAppProxy.addEventListener("mozContentEvent",
|
||||
this._notifyPayFlowClosed);
|
||||
this._notifyPayFlowClosed);
|
||||
|
||||
SystemAppProxy.dispatchEvent(detail);
|
||||
},
|
||||
|
||||
closePaymentFlow: function(aRequestId) {
|
||||
return new Promise((aResolve) => {
|
||||
// After receiving the payment provider confirmation about the
|
||||
// successful or failed payment flow, we notify the UI to close the
|
||||
// payment flow dialog and return to the caller application.
|
||||
let id = kClosePaymentFlowEvent + "-" + uuidgen.generateUUID().toString();
|
||||
|
||||
let detail = {
|
||||
type: kClosePaymentFlowEvent,
|
||||
id: id,
|
||||
requestId: aRequestId
|
||||
};
|
||||
|
||||
// In order to avoid race conditions, we wait for the UI to notify that
|
||||
// it has successfully closed the payment flow and has recovered the
|
||||
// caller app, before notifying the parent process to fire the success
|
||||
// or error event over the DOMRequest.
|
||||
SystemAppProxy.addEventListener("mozContentEvent",
|
||||
(function closePaymentFlowReturn() {
|
||||
SystemAppProxy.removeEventListener("mozContentEvent",
|
||||
closePaymentFlowReturn);
|
||||
this.cleanup();
|
||||
aResolve();
|
||||
}).bind(this));
|
||||
|
||||
SystemAppProxy.dispatchEvent(detail);
|
||||
});
|
||||
},
|
||||
|
||||
cleanup: function cleanup() {
|
||||
if (this._handleSelection) {
|
||||
SystemAppProxy.removeEventListener("mozContentEvent", this._handleSelection);
|
||||
SystemAppProxy.removeEventListener("mozContentEvent",
|
||||
this._handleSelection);
|
||||
this._handleSelection = null;
|
||||
}
|
||||
|
||||
if (this._notifyPayFlowClosed) {
|
||||
SystemAppProxy.removeEventListener("mozContentEvent", 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])
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/* 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;
|
||||
|
||||
const PREF_DEBUG = "dom.payment.debug";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
|
||||
"@mozilla.org/ril/content-helper;1",
|
||||
"nsIIccProvider");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gRil",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
|
||||
"@mozilla.org/settingsService;1",
|
||||
"nsISettingsService");
|
||||
|
||||
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
|
||||
const kRilDefaultDataServiceId = "ril.data.defaultServiceId";
|
||||
const kRilDefaultPaymentServiceId = "ril.payment.defaultServiceId";
|
||||
|
||||
let _debug;
|
||||
try {
|
||||
_debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
|
||||
&& Services.prefs.getBoolPref(PREF_DEBUG);
|
||||
} catch(e){
|
||||
_debug = false;
|
||||
}
|
||||
|
||||
function LOG(s) {
|
||||
if (!_debug) {
|
||||
return;
|
||||
}
|
||||
dump("== Payment Provider == " + s + "\n");
|
||||
}
|
||||
|
||||
function LOGE(s) {
|
||||
dump("== Payment Provider ERROR == " + s + "\n");
|
||||
}
|
||||
|
||||
function PaymentSettings() {
|
||||
Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
|
||||
|
||||
[kRilDefaultDataServiceId, kRilDefaultPaymentServiceId].forEach(setting => {
|
||||
gSettingsService.createLock().get(setting, this);
|
||||
});
|
||||
}
|
||||
|
||||
PaymentSettings.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISettingsServiceCallback,
|
||||
Ci.nsIObserver]),
|
||||
|
||||
dataServiceId: 0,
|
||||
_paymentServiceId: 0,
|
||||
|
||||
get paymentServiceId() {
|
||||
return this._paymentServiceId;
|
||||
},
|
||||
|
||||
set paymentServiceId(serviceId) {
|
||||
// We allow the payment provider to set the service ID that will be used
|
||||
// for the payment process.
|
||||
// This service ID will be the one used by the silent SMS flow.
|
||||
// If the payment is done with an external SIM, the service ID must be set
|
||||
// to null.
|
||||
if (serviceId != null && serviceId >= gRil.numRadioInterfaces) {
|
||||
LOGE("Invalid service ID " + serviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
gSettingsService.createLock().set(kRilDefaultPaymentServiceId,
|
||||
serviceId, 0);
|
||||
this._paymentServiceId = serviceId;
|
||||
},
|
||||
|
||||
setServiceId: function(aName, aValue) {
|
||||
switch (aName) {
|
||||
case kRilDefaultDataServiceId:
|
||||
this.dataServiceId = aValue;
|
||||
if (_debug) {
|
||||
LOG("dataServiceId " + this.dataServiceId);
|
||||
}
|
||||
break;
|
||||
case kRilDefaultPaymentServiceId:
|
||||
this._paymentServiceId = aValue;
|
||||
if (_debug) {
|
||||
LOG("paymentServiceId " + this._paymentServiceId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handle: function(aName, aValue) {
|
||||
if (aName != kRilDefaultDataServiceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setServiceId(aName, aValue);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != kMozSettingsChangedObserverTopic) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if ("wrappedJSObject" in aSubject) {
|
||||
aSubject = aSubject.wrappedJSObject;
|
||||
}
|
||||
if (!aSubject.key ||
|
||||
(aSubject.key !== kRilDefaultDataServiceId &&
|
||||
aSubject.key !== kRilDefaultPaymentServiceId)) {
|
||||
return;
|
||||
}
|
||||
this.setServiceId(aSubject.key, aSubject.value);
|
||||
} catch (e) {
|
||||
LOGE(e);
|
||||
}
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
|
||||
}
|
||||
};
|
||||
|
||||
function PaymentProviderStrategy() {
|
||||
this._settings = new PaymentSettings();
|
||||
}
|
||||
|
||||
PaymentProviderStrategy.prototype = {
|
||||
get paymentServiceId() {
|
||||
return this._settings.paymentServiceId;
|
||||
},
|
||||
|
||||
set paymentServiceId(aServiceId) {
|
||||
this._settings.paymentServiceId = aServiceId;
|
||||
},
|
||||
|
||||
get iccInfo() {
|
||||
if (!this._iccInfo) {
|
||||
this._iccInfo = [];
|
||||
for (let i = 0; i < gRil.numRadioInterfaces; i++) {
|
||||
let info = iccProvider.getIccInfo(i);
|
||||
if (!info) {
|
||||
LOGE("Tried to get the ICC info for an invalid service ID " + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
this._iccInfo.push({
|
||||
iccId: info.iccid,
|
||||
mcc: info.mcc,
|
||||
mnc: info.mnc,
|
||||
dataPrimary: i == this._settings.dataServiceId
|
||||
});
|
||||
}
|
||||
}
|
||||
return this._iccInfo;
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
this._settings.cleanup();
|
||||
},
|
||||
|
||||
classID: Components.ID("{4834b2e1-2c91-44ea-b020-e2581ed279a4}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentProviderStrategy])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentProviderStrategy]);
|
|
@ -21,6 +21,7 @@ EXTRA_COMPONENTS += [
|
|||
'MobileIdentityUIGlue.js',
|
||||
'OMAContentHandler.js',
|
||||
'PaymentGlue.js',
|
||||
'PaymentProviderStrategy.js',
|
||||
'ProcessGlobal.js',
|
||||
'SmsProtocolHandler.js',
|
||||
'SystemMessageGlue.js',
|
||||
|
|
|
@ -604,6 +604,7 @@
|
|||
|
||||
@BINPATH@/components/Payment.js
|
||||
@BINPATH@/components/PaymentFlowInfo.js
|
||||
@BINPATH@/components/PaymentProvider.js
|
||||
@BINPATH@/components/Payment.manifest
|
||||
|
||||
@BINPATH@/components/DownloadsAPI.js
|
||||
|
@ -854,6 +855,7 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
|||
@BINPATH@/components/ProcessGlobal.js
|
||||
@BINPATH@/components/OMAContentHandler.js
|
||||
@BINPATH@/components/PaymentGlue.js
|
||||
@BINPATH@/components/PaymentProviderStrategy.js
|
||||
@BINPATH@/components/RecoveryService.js
|
||||
@BINPATH@/components/MailtoProtocolHandler.js
|
||||
@BINPATH@/components/SmsProtocolHandler.js
|
||||
|
|
|
@ -13767,3 +13767,35 @@ nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString &aProvider,
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetPaymentRequestId(const nsAString& aPaymentRequestId)
|
||||
{
|
||||
mPaymentRequestId = aPaymentRequestId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsString
|
||||
nsDocShell::GetInheritedPaymentRequestId()
|
||||
{
|
||||
if (!mPaymentRequestId.IsEmpty()) {
|
||||
return mPaymentRequestId;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
|
||||
GetSameTypeParent(getter_AddRefs(parentAsItem));
|
||||
|
||||
nsCOMPtr<nsIDocShell> parent = do_QueryInterface(parentAsItem);
|
||||
if (!parent) {
|
||||
return mPaymentRequestId;
|
||||
}
|
||||
return static_cast<nsDocShell*>(
|
||||
parent.get())->GetInheritedPaymentRequestId();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetPaymentRequestId(nsAString& aPaymentRequestId)
|
||||
{
|
||||
aPaymentRequestId = GetInheritedPaymentRequestId();
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -947,6 +947,9 @@ protected:
|
|||
// find it by walking up the docshell hierarchy.)
|
||||
uint32_t mOwnOrContainingAppId;
|
||||
|
||||
nsString mPaymentRequestId;
|
||||
|
||||
nsString GetInheritedPaymentRequestId();
|
||||
private:
|
||||
nsCString mForcedCharset;
|
||||
nsCString mParentCharset;
|
||||
|
|
|
@ -54,7 +54,7 @@ interface nsITabParent;
|
|||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(fef3bae1-6673-4c49-9f5a-fcc075926730)]
|
||||
[scriptable, builtinclass, uuid(e0e833fe-3a5b-48b0-8684-a097e09c0723)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
|
@ -773,7 +773,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
|
||||
/**
|
||||
* Returns true if this docshell corresponds to an <iframe mozbrowser> or
|
||||
* <iframe mozap>, or if this docshell is contained in an <iframe mozbrowser>
|
||||
* <iframe mozapp>, or if this docshell is contained in an <iframe mozbrowser>
|
||||
* or <iframe mozapp>.
|
||||
*
|
||||
* To compute this value, we walk up the docshell hierarchy. If we encounter
|
||||
|
@ -840,7 +840,7 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
*/
|
||||
nsIDocShell getSameTypeParentIgnoreBrowserAndAppBoundaries();
|
||||
|
||||
/**
|
||||
/**
|
||||
* True iff asynchronous panning and zooming is enabled for this
|
||||
* docshell.
|
||||
*/
|
||||
|
@ -1049,4 +1049,8 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
*/
|
||||
[infallible] readonly attribute boolean hasLoadedNonBlankURI;
|
||||
|
||||
/**
|
||||
* Holds the id of the payment request associated with this docshell if any.
|
||||
*/
|
||||
attribute DOMString paymentRequestId;
|
||||
};
|
||||
|
|
|
@ -4,3 +4,6 @@ category JavaScript-navigator-property mozPay @mozilla.org/payment/content-helpe
|
|||
|
||||
component {b8bce4e7-fbf0-4719-a634-b1bf9018657c} PaymentFlowInfo.js
|
||||
contract @mozilla.org/payment/flow-info;1 {b8bce4e7-fbf0-4719-a634-b1bf9018657c}
|
||||
|
||||
component {82144756-72ab-45b7-8621-f3dad431dd2f} PaymentProvider.js
|
||||
contract @mozilla.org/payment/provider;1 {82144756-72ab-45b7-8621-f3dad431dd2f}
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
/* 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, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
|
||||
const PREF_DEBUG = "dom.payment.debug";
|
||||
|
||||
let _debug;
|
||||
try {
|
||||
_debug = Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
|
||||
&& Services.prefs.getBoolPref(PREF_DEBUG);
|
||||
} catch(e) {
|
||||
_debug = false;
|
||||
}
|
||||
|
||||
function DEBUG(s) {
|
||||
if (!_debug) {
|
||||
return;
|
||||
}
|
||||
dump("== Payment Provider == " + s + "\n");
|
||||
}
|
||||
|
||||
function DEBUG_E(s) {
|
||||
dump("== Payment Provider ERROR == " + s + "\n");
|
||||
}
|
||||
|
||||
const kPaymentFlowCancelled = "payment-flow-cancelled";
|
||||
|
||||
function PaymentProvider() {
|
||||
}
|
||||
|
||||
PaymentProvider.prototype = {
|
||||
init: function(aWindow) {
|
||||
let docshell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
this._requestId = docshell.paymentRequestId;
|
||||
this._oncancelObserver = this.oncancel.bind(this);
|
||||
Services.obs.addObserver(this._oncancelObserver,
|
||||
kPaymentFlowCancelled,
|
||||
false);
|
||||
this._strategy = Cc["@mozilla.org/payment/provider-strategy;1"]
|
||||
.createInstance(Ci.nsIPaymentProviderStrategy);
|
||||
this._window = aWindow;
|
||||
},
|
||||
|
||||
paymentSuccess: function(aResult) {
|
||||
_debug && DEBUG("paymentSuccess " + aResult);
|
||||
let glue = Cc["@mozilla.org/payment/ui-glue;1"]
|
||||
.createInstance(Ci.nsIPaymentUIGlue);
|
||||
glue.closePaymentFlow(this._requestId).then(() => {
|
||||
if (!this._requestId) {
|
||||
return;
|
||||
}
|
||||
cpmm.sendAsyncMessage("Payment:Success", { result: aResult,
|
||||
requestId: this._requestId });
|
||||
});
|
||||
},
|
||||
|
||||
paymentFailed: function(aError) {
|
||||
_debug && DEBUG("paymentFailed " + aError);
|
||||
let glue = Cc["@mozilla.org/payment/ui-glue;1"]
|
||||
.createInstance(Ci.nsIPaymentUIGlue);
|
||||
glue.closePaymentFlow(this._requestId).then(() => {
|
||||
if (!this._requestId) {
|
||||
return;
|
||||
}
|
||||
cpmm.sendAsyncMessage("Payment:Failed", { errorMsg: aError,
|
||||
requestId: this._requestId });
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
get paymentServiceId() {
|
||||
return this._strategy.paymentServiceId;
|
||||
},
|
||||
|
||||
set paymentServiceId(aServiceId) {
|
||||
this._strategy.paymentServiceId = aServiceId;
|
||||
},
|
||||
|
||||
/**
|
||||
* We expose to the payment provider the information of all the SIMs
|
||||
* available in the device. iccInfo is an object of this form:
|
||||
* {
|
||||
* "serviceId1": {
|
||||
* mcc: <string>,
|
||||
* mnc: <string>,
|
||||
* iccId: <string>,
|
||||
* dataPrimary: <boolean>
|
||||
* },
|
||||
* "serviceIdN": {...}
|
||||
* }
|
||||
*/
|
||||
get iccInfo() {
|
||||
return this._strategy.iccInfo;
|
||||
},
|
||||
|
||||
oncancel: function() {
|
||||
_debug && DEBUG("Cleaning up!");
|
||||
|
||||
this._strategy.cleanup();
|
||||
Services.obs.removeObserver(this._oncancelObserver, kPaymentFlowCancelled);
|
||||
if (this._cleanup) {
|
||||
this._cleanup();
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{82144756-72ab-45b7-8621-f3dad431dd2f}"),
|
||||
|
||||
contractID: "@mozilla.org/payment/provider;1",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIDOMGlobalPropertyInitializer])
|
||||
};
|
||||
|
||||
#if defined(MOZ_B2G_RIL) || defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "smsService",
|
||||
"@mozilla.org/sms/smsservice;1",
|
||||
"nsISmsService");
|
||||
|
||||
const kSilentSmsReceivedTopic = "silent-sms-received";
|
||||
|
||||
// In order to send messages through nsISmsService, we need to implement
|
||||
// nsIMobileMessageCallback, as the WebSMS API implementation is not usable
|
||||
// from JS.
|
||||
function SilentSmsRequest(aDOMRequest) {
|
||||
this.request = aDOMRequest;
|
||||
}
|
||||
|
||||
SilentSmsRequest.prototype = {
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileMessageCallback]),
|
||||
|
||||
classID: Components.ID("{1d58889c-5660-4cca-a8fd-97ef63e5d3b2}"),
|
||||
|
||||
notifyMessageSent: function notifyMessageSent(aMessage) {
|
||||
_debug && DEBUG("Silent message successfully sent");
|
||||
Services.DOMRequest.fireSuccessAsync(this.request, aMessage);
|
||||
},
|
||||
|
||||
notifySendMessageFailed: function notifySendMessageFailed(aError) {
|
||||
DEBUG_E("Error sending silent message " + aError);
|
||||
Services.DOMRequest.fireErrorAsync(this.request, aError);
|
||||
}
|
||||
};
|
||||
|
||||
PaymentProvider.prototype._silentNumbers = null;
|
||||
|
||||
PaymentProvider.prototype._silentSmsObservers = null;
|
||||
|
||||
PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
|
||||
_debug && DEBUG("Sending silent message " + aNumber + " - " + aMessage);
|
||||
|
||||
let request = Services.DOMRequest.createRequest(this._window);
|
||||
|
||||
if (this._strategy.paymentServiceId === null) {
|
||||
DEBUG_E("No payment service ID set. Cannot send silent SMS");
|
||||
Services.DOMRequest.fireErrorAsync(request,
|
||||
"NO_PAYMENT_SERVICE_ID");
|
||||
return request;
|
||||
}
|
||||
|
||||
let smsRequest = new SilentSmsRequest(request);
|
||||
smsService.send(this._strategy.paymentServiceId, aNumber, aMessage, true,
|
||||
smsRequest);
|
||||
return request;
|
||||
};
|
||||
|
||||
PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
|
||||
_debug && DEBUG("observeSilentSms " + aNumber);
|
||||
|
||||
if (!this._silentSmsObservers) {
|
||||
this._silentSmsObservers = {};
|
||||
this._silentNumbers = [];
|
||||
this._onSilentSmsObserver = this._onSilentSms.bind(this);
|
||||
Services.obs.addObserver(this._onSilentSmsObserver,
|
||||
kSilentSmsReceivedTopic,
|
||||
false);
|
||||
}
|
||||
|
||||
if (!this._silentSmsObservers[aNumber]) {
|
||||
this._silentSmsObservers[aNumber] = [];
|
||||
this._silentNumbers.push(aNumber);
|
||||
smsService.addSilentNumber(aNumber);
|
||||
}
|
||||
|
||||
if (this._silentSmsObservers[aNumber].indexOf(aCallback) == -1) {
|
||||
this._silentSmsObservers[aNumber].push(aCallback);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
|
||||
_debug && DEBUG("removeSilentSmsObserver " + aNumber);
|
||||
|
||||
if (!this._silentSmsObservers || !this._silentSmsObservers[aNumber]) {
|
||||
_debug && DEBUG("No observers for " + aNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
let index = this._silentSmsObservers[aNumber].indexOf(aCallback);
|
||||
if (index != -1) {
|
||||
this._silentSmsObservers[aNumber].splice(index, 1);
|
||||
if (this._silentSmsObservers[aNumber].length == 0) {
|
||||
this._silentSmsObservers[aNumber] = null;
|
||||
this._silentNumbers.splice(this._silentNumbers.indexOf(aNumber), 1);
|
||||
smsService.removeSilentNumber(aNumber);
|
||||
}
|
||||
} else if (_debug) {
|
||||
DEBUG("No callback found for " + aNumber);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
PaymentProvider.prototype._onSilentSms = function(aSubject, aTopic, aData) {
|
||||
_debug && DEBUG("Got silent message! " + aSubject.sender + " - " + aSubject.body);
|
||||
|
||||
let number = aSubject.sender;
|
||||
if (!number || this._silentNumbers.indexOf(number) == -1) {
|
||||
_debug && DEBUG("No observers for " + number);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the service ID is null it means that the payment provider asked the
|
||||
// user for her MSISDN, so we are in a MT only SMS auth flow. In this case
|
||||
// we manually set the service ID to the one corresponding with the SIM
|
||||
// that received the SMS.
|
||||
if (this._strategy.paymentServiceId === null) {
|
||||
let i = 0;
|
||||
while(i < gRil.numRadioInterfaces) {
|
||||
if (this.iccInfo[i].iccId === aSubject.iccId) {
|
||||
this._strategy.paymentServiceId = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this._silentSmsObservers[number].forEach(function(callback) {
|
||||
callback(aSubject);
|
||||
});
|
||||
};
|
||||
|
||||
PaymentProvider.prototype._cleanup = function() {
|
||||
if (!this._silentNumbers) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (this._silentNumbers.length) {
|
||||
let number = this._silentNumbers.pop();
|
||||
smsService.removeSilentNumber(number);
|
||||
}
|
||||
this._silentNumbers = null;
|
||||
this._silentSmsObservers = null;
|
||||
Services.obs.removeObserver(this._onSilentSmsObserver,
|
||||
kSilentSmsReceivedTopic);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
PaymentProvider.prototype.sendSilentSms = function(aNumber, aMessage) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
PaymentProvider.prototype.observeSilentSms = function(aNumber, aCallback) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
PaymentProvider.prototype.removeSilentSmsObserver = function(aNumber, aCallback) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentProvider]);
|
|
@ -0,0 +1,28 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/NavigatorBinding.h"
|
||||
#include "PaymentProviderUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsIDocShell.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/* static */ bool
|
||||
PaymentProviderUtils::EnabledForScope(JSContext* aCx,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win =
|
||||
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
|
||||
NS_ENSURE_TRUE(win, false);
|
||||
|
||||
nsIDocShell *docShell = win->GetDocShell();
|
||||
NS_ENSURE_TRUE(docShell, false);
|
||||
|
||||
nsString paymentRequestId;
|
||||
docShell->GetPaymentRequestId(paymentRequestId);
|
||||
|
||||
return !paymentRequestId.IsEmpty();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_payment_PaymentProviderEnabler_h
|
||||
#define mozilla_dom_payment_PaymentProviderEnabler_h
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class PaymentProviderUtils
|
||||
{
|
||||
public:
|
||||
static bool EnabledForScope(JSContext*, JSObject*);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_payment_PaymentProviderEnabler_h
|
|
@ -7,6 +7,7 @@
|
|||
XPIDL_SOURCES += [
|
||||
'nsINavigatorPayment.idl',
|
||||
'nsIPaymentFlowInfo.idl',
|
||||
'nsIPaymentProviderStrategy.idl',
|
||||
'nsIPaymentUIGlue.idl',
|
||||
]
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(c3971bd9-0fbf-48d3-9498-0ac340d0d216)]
|
||||
interface nsIPaymentProviderStrategy : nsISupports
|
||||
{
|
||||
attribute DOMString paymentServiceId;
|
||||
readonly attribute jsval iccInfo;
|
||||
|
||||
void cleanup();
|
||||
};
|
|
@ -12,7 +12,7 @@ interface nsIPaymentUIGlueCallback : nsISupports
|
|||
void onresult(in DOMString requestId, in DOMString result);
|
||||
};
|
||||
|
||||
[scriptable, uuid(4dda9aa0-df88-4dcd-a583-199e516fa438)]
|
||||
[scriptable, uuid(4dc09e33-d395-4e1d-acb4-e85415181270)]
|
||||
interface nsIPaymentUIGlue : nsISupports
|
||||
{
|
||||
// The 'paymentRequestsInfo' contains the payment request information
|
||||
|
@ -26,5 +26,7 @@ interface nsIPaymentUIGlue : nsISupports
|
|||
in nsIPaymentFlowInfo paymentFlowInfo,
|
||||
in nsIPaymentUIGlueCallback errorCb);
|
||||
|
||||
void cleanup();
|
||||
// The promise resolves with no value as soon as the payment window is
|
||||
// closed.
|
||||
jsval /*Promise*/ closePaymentFlow(in DOMString requestId);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,14 @@
|
|||
|
||||
DIRS += ['interfaces']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'PaymentProviderUtils.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'PaymentProviderUtils.cpp',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'Payment.jsm',
|
||||
]
|
||||
|
@ -13,7 +21,19 @@ EXTRA_PP_JS_MODULES += [
|
|||
EXTRA_COMPONENTS += [
|
||||
'Payment.js',
|
||||
'Payment.manifest',
|
||||
'PaymentFlowInfo.js',
|
||||
'PaymentFlowInfo.js'
|
||||
]
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'PaymentProvider.js'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base'
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<!--
|
||||
* 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/. */
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test app for bug 1097928 - Check if MozPaymentProvider API is exposed</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id='test'>
|
||||
<script type="application/javascript;version=1.8">
|
||||
function receiveMessage(event) {
|
||||
let message = JSON.parse(event.data);
|
||||
let exposed = (navigator.mozPaymentProvider != undefined);
|
||||
window.parent.postMessage(JSON.stringify({
|
||||
iframeType: message.iframeType,
|
||||
exposed: exposed
|
||||
}), "*");
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage, false, true);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
navigator.mozPaymentProvider.paymentFailed();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
navigator.mozPaymentProvider.paymentSuccess("aResult");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
[DEFAULT]
|
||||
skip-if=true # This test uses MockPaymentsUIGlue which uses __exposedProps__
|
||||
# we need to move this to a chrome test, but these are not
|
||||
# available in b2g or android for now.
|
||||
|
||||
support-files=
|
||||
file_mozpayproviderchecker.html
|
||||
file_payprovidersuccess.html
|
||||
file_payproviderfailure.html
|
||||
|
||||
[test_mozpaymentprovider.html]
|
||||
[test_mozpay_callbacks.html]
|
|
@ -0,0 +1,108 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1097928
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for navigator.mozPay. Bug 1097928</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let tests = [() => {
|
||||
/*
|
||||
{
|
||||
"iss": "323d34dc-b5cf-4822-8e47-6a4515dc74db",
|
||||
"typ": "mozilla/payments/test/success",
|
||||
"request": {
|
||||
"name": "Virtual Kiwi",
|
||||
"id": "af1f960a-3f90-4e2d-a20f-d5170aee49f2",
|
||||
"postbackURL": "https://inapp-pay-test.paas.allizom.org/mozpay",
|
||||
"productData": "localTransID=14546cd1-db9b-4759-986f-2a6a295fdcc1",
|
||||
"chargebackURL": "https://inapp-pay-test.paas.allizom.org/mozpay",
|
||||
"description": "The forbidden fruit"
|
||||
}
|
||||
}
|
||||
*/
|
||||
let jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIzMjNkMzRkYy1iNWNmLTQ4MjItOGU0Ny02YTQ1MTVkYzc0ZGIiLCJyZXF1ZXN0Ijp7ImRlc2NyaXB0aW9uIjoiVGhlIGZvcmJpZGRlbiBmcnVpdCIsImlkIjoiYWYxZjk2MGEtM2Y5MC00ZTJkLWEyMGYtZDUxNzBhZWU0OWYyIiwicG9zdGJhY2tVUkwiOiJodHRwczovL2luYXBwLXBheS10ZXN0LnBhYXMuYWxsaXpvbS5vcmcvbW96cGF5L3Bvc3RiYWNrIiwicHJvZHVjdERhdGEiOiJsb2NhbFRyYW5zSUQ9MTQ1NDZjZDEtZGI5Yi00NzU5LTk4NmYtMmE2YTI5NWZkY2MxIiwiY2hhcmdlYmFja1VSTCI6Imh0dHBzOi8vaW5hcHAtcGF5LXRlc3QucGFhcy5hbGxpem9tLm9yZy9tb3pwYXkvY2hhcmdlYmFjayIsIm5hbWUiOiJWaXJ0dWFsIEtpd2kifSwidHlwIjoibW96aWxsYS9wYXltZW50cy90ZXN0L3N1Y2Nlc3MifQ.8zaeYFUCwKkZWk2TFf2wEJWrmiSYQGNbpKc2ADkvL9s";
|
||||
let req = window.navigator.mozPay(jwt);
|
||||
req.onsuccess = (result) => {
|
||||
ok(true, "Expected mozPay success");
|
||||
runTest();
|
||||
};
|
||||
req.onerror = (error) => {
|
||||
ok(false, "Unexpected mozPay error " + error);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
}, () => {
|
||||
/*
|
||||
{
|
||||
"iss": "323d34dc-b5cf-4822-8e47-6a4515dc74db",
|
||||
"typ": "mozilla/payments/test/failure",
|
||||
"request": {
|
||||
"name": "Virtual Kiwi",
|
||||
"id": "af1f960a-3f90-4e2d-a20f-d5170aee49f2",
|
||||
"postbackURL": "https://inapp-pay-test.paas.allizom.org/mozpay",
|
||||
"productData": "localTransID=cc3c0994-33e8-4a21-aa2c-75ee44f5fe75",
|
||||
"chargebackURL": "https://inapp-pay-test.paas.allizom.org/mozpay",
|
||||
"description": "The forbidden fruit"
|
||||
}
|
||||
}
|
||||
*/
|
||||
let jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIzMjNkMzRkYy1iNWNmLTQ4MjItOGU0Ny02YTQ1MTVkYzc0ZGIiLCJyZXF1ZXN0Ijp7ImRlc2NyaXB0aW9uIjoiVGhlIGZvcmJpZGRlbiBmcnVpdCIsImlkIjoiYWYxZjk2MGEtM2Y5MC00ZTJkLWEyMGYtZDUxNzBhZWU0OWYyIiwicG9zdGJhY2tVUkwiOiJodHRwczovL2luYXBwLXBheS10ZXN0LnBhYXMuYWxsaXpvbS5vcmcvbW96cGF5L3Bvc3RiYWNrIiwicHJvZHVjdERhdGEiOiJsb2NhbFRyYW5zSUQ9Y2MzYzA5OTQtMzNlOC00YTIxLWFhMmMtNzVlZTQ0ZjVmZTc1IiwiY2hhcmdlYmFja1VSTCI6Imh0dHBzOi8vaW5hcHAtcGF5LXRlc3QucGFhcy5hbGxpem9tLm9yZy9tb3pwYXkvY2hhcmdlYmFjayIsIm5hbWUiOiJWaXJ0dWFsIEtpd2kifSwidHlwIjoibW96aWxsYS9wYXltZW50cy90ZXN0L2ZhaWx1cmUifQ.1uV4-HkmwO0oDv50wi1Ma4tNpnxoFGaw5zaPj8xkcAc";
|
||||
let req = window.navigator.mozPay(jwt);
|
||||
req.onsuccess = (result) => {
|
||||
ok(false, "Unexpected mozPay success " + result);
|
||||
SimpleTest.finish();
|
||||
};
|
||||
req.onerror = (error) => {
|
||||
ok(true, "Expected mozPay error");
|
||||
runTest();
|
||||
};
|
||||
}];
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
ok(true, "Done!");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
tests.shift()();
|
||||
}
|
||||
|
||||
SpecialPowers.MockPaymentsUIGlue.init(window);
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
"set": [
|
||||
["dom.payment.skipHTTPSCheck", "true"],
|
||||
["dom.payment.debug", "true"],
|
||||
["dom.payment.provider.1.name", "SuccessProvider"],
|
||||
["dom.payment.provider.1.description", ""],
|
||||
["dom.payment.provider.1.uri",
|
||||
"http://mochi.test:8888/tests/dom/payment/tests/mochitest/file_payprovidersuccess.html?req="],
|
||||
["dom.payment.provider.1.type", "mozilla/payments/test/success"],
|
||||
["dom.payment.provider.1.requestMethod", "GET"],
|
||||
["dom.payment.provider.2.name", "FailureProvider"],
|
||||
["dom.payment.provider.2.description", ""],
|
||||
["dom.payment.provider.2.uri",
|
||||
"http://mochi.test:8888/tests/dom/payment/tests/mochitest/file_payproviderfailure.html?req="],
|
||||
["dom.payment.provider.2.type", "mozilla/payments/test/failure"],
|
||||
["dom.payment.provider.2.requestMethod", "GET"],
|
||||
]
|
||||
}, () => {
|
||||
runTest();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1097928
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for navigator.mozPaymentProvider exposure. Bug 1097928</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// We create two iframes. The first one is a regular iframe with no payment
|
||||
// information and so it should not have access to the MozPaymentProvider
|
||||
// API. For the second iframe we set a dummy payment request ID which should
|
||||
// expose the MozPaymentProvider API.
|
||||
let tests = [function() {
|
||||
// Iframe with no payment information.
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("mozbrowser", "true");
|
||||
iframe.src = "file_mozpayproviderchecker.html";
|
||||
|
||||
document.getElementById("content").appendChild(iframe);
|
||||
|
||||
iframe.addEventListener("load", function onLoad() {
|
||||
iframe.removeEventListener("load", onLoad);
|
||||
iframe.contentWindow.postMessage(JSON.stringify({
|
||||
iframeType: "regular"
|
||||
}), "*");
|
||||
}, false);
|
||||
}, function() {
|
||||
// Payment iframe.
|
||||
let paymentIframe = document.createElement("iframe");
|
||||
paymentIframe.setAttribute("mozbrowser", "true");
|
||||
paymentIframe.src = "file_mozpayproviderchecker.html";
|
||||
|
||||
document.getElementById("content").appendChild(paymentIframe);
|
||||
|
||||
let Ci = SpecialPowers.Ci;
|
||||
let docshell = SpecialPowers.wrap(paymentIframe.contentWindow)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
docshell.paymentRequestId = "dummyid";
|
||||
|
||||
paymentIframe.addEventListener("load", function onLoad() {
|
||||
paymentIframe.removeEventListener("load", onLoad);
|
||||
paymentIframe.contentWindow.postMessage(JSON.stringify({
|
||||
iframeType: "payment"
|
||||
}), "*");
|
||||
}, false);
|
||||
}];
|
||||
|
||||
function receiveMessage(event) {
|
||||
let message = JSON.parse(event.data);
|
||||
switch (message.iframeType) {
|
||||
case "regular":
|
||||
ok(!message.exposed, "MozPaymentProvider is not exposed in regular iframe");
|
||||
break;
|
||||
case "payment":
|
||||
ok(message.exposed, "MozPaymentProvider is exposed in payment iframe");
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unexpected iframe type");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
runTest();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
if (!tests.length) {
|
||||
ok(true, "Done!");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
tests.shift()();
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage, false, true);
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
callback SilentSmsCallback = void (optional MozSmsMessage message);
|
||||
|
||||
dictionary PaymentIccInfo {
|
||||
DOMString mcc;
|
||||
DOMString mnc;
|
||||
DOMString iccId;
|
||||
boolean dataPrimary;
|
||||
};
|
||||
|
||||
[NavigatorProperty="mozPaymentProvider",
|
||||
NoInterfaceObject,
|
||||
HeaderFile="mozilla/dom/PaymentProviderUtils.h",
|
||||
Func="mozilla::dom::PaymentProviderUtils::EnabledForScope",
|
||||
JSImplementation="@mozilla.org/payment/provider;1"]
|
||||
interface PaymentProvider {
|
||||
readonly attribute DOMString? paymentServiceId;
|
||||
// We expose to the payment provider the information of all the SIMs
|
||||
// available in the device.
|
||||
[Cached, Pure] readonly attribute sequence<PaymentIccInfo>? iccInfo;
|
||||
|
||||
void paymentSuccess(optional DOMString result);
|
||||
void paymentFailed(optional DOMString error);
|
||||
|
||||
DOMRequest sendSilentSms(DOMString number, DOMString message);
|
||||
void observeSilentSms(DOMString number, SilentSmsCallback callback);
|
||||
void removeSilentSmsObserver(DOMString number, SilentSmsCallback callback);
|
||||
};
|
|
@ -809,3 +809,8 @@ if CONFIG['MOZ_EME']:
|
|||
'MediaKeySession.webidl',
|
||||
'MediaKeySystemAccess.webidl',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_PAY']:
|
||||
WEBIDL_FILES += [
|
||||
'MozPaymentProvider.webidl'
|
||||
]
|
||||
|
|
|
@ -32,4 +32,5 @@ marionette.jar:
|
|||
modules/MockFilePicker.jsm (../specialpowers/content/MockFilePicker.jsm)
|
||||
modules/MockColorPicker.jsm (../specialpowers/content/MockColorPicker.jsm)
|
||||
modules/MockPermissionPrompt.jsm (../specialpowers/content/MockPermissionPrompt.jsm)
|
||||
modules/MockPaymentsUIGlue.jsm (../specialpowers/content/MockPaymentsUIGlue.jsm)
|
||||
modules/Assert.jsm (../modules/Assert.jsm)
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/* 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/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MockPaymentsUIGlue"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cm = Components.manager;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const CONTRACT_ID = "@mozilla.org/payment/ui-glue;1";
|
||||
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
let classID;
|
||||
let oldFactory;
|
||||
let newFactory = function(window) {
|
||||
return {
|
||||
createInstance: function(aOuter, aIID) {
|
||||
if (aOuter) {
|
||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return new MockPaymentsUIGlueInstance(window).QueryInterface(aIID);
|
||||
},
|
||||
lockFactory: function(aLock) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
|
||||
};
|
||||
};
|
||||
|
||||
this.MockPaymentsUIGlue = {
|
||||
init: function(aWindow) {
|
||||
try {
|
||||
classID = registrar.contractIDToCID(CONTRACT_ID);
|
||||
oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
|
||||
} catch (ex) {
|
||||
oldClassID = "";
|
||||
oldFactory = null;
|
||||
dump("TEST-INFO | can't get payments ui glue registered component, " +
|
||||
"assuming there is none");
|
||||
}
|
||||
if (oldFactory) {
|
||||
registrar.unregisterFactory(classID, oldFactory);
|
||||
}
|
||||
registrar.registerFactory(classID, "", CONTRACT_ID,
|
||||
new newFactory(aWindow));
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
},
|
||||
|
||||
cleanup: function() {
|
||||
this.reset();
|
||||
if (oldFactory) {
|
||||
registrar.unregisterFactory(classID, newFactory);
|
||||
registrar.registerFactory(classID, "", CONTRACT_ID, oldFactory);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function MockPaymentsUIGlueInstance(aWindow) {
|
||||
this.window = aWindow;
|
||||
};
|
||||
|
||||
MockPaymentsUIGlueInstance.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]),
|
||||
|
||||
confirmPaymentRequest: function(aRequestId,
|
||||
aRequests,
|
||||
aSuccessCb,
|
||||
aErrorCb) {
|
||||
aSuccessCb.onresult(aRequestId, aRequests[0].type);
|
||||
},
|
||||
|
||||
showPaymentFlow: function(aRequestId,
|
||||
aPaymentFlowInfo,
|
||||
aErrorCb) {
|
||||
let document = this.window.document;
|
||||
let frame = document.createElement("iframe");
|
||||
frame.setAttribute("mozbrowser", true);
|
||||
frame.setAttribute("remote", true);
|
||||
document.body.appendChild(frame);
|
||||
let docshell = frame.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
docshell.paymentRequestId = aRequestId;
|
||||
frame.src = aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt;
|
||||
},
|
||||
|
||||
closePaymentFlow: function(aRequestId) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
// Expose everything to content. We call reset() here so that all of the relevant
|
||||
// lazy expandos get added.
|
||||
MockPaymentsUIGlue.reset();
|
||||
function exposeAll(obj) {
|
||||
var props = {};
|
||||
for (var prop in obj)
|
||||
props[prop] = 'rw';
|
||||
obj.__exposedProps__ = props;
|
||||
}
|
||||
exposeAll(MockPaymentsUIGlue);
|
||||
exposeAll(MockPaymentsUIGlueInstance.prototype);
|
|
@ -14,6 +14,7 @@ var Cu = Components.utils;
|
|||
Cu.import("resource://specialpowers/MockFilePicker.jsm");
|
||||
Cu.import("resource://specialpowers/MockColorPicker.jsm");
|
||||
Cu.import("resource://specialpowers/MockPermissionPrompt.jsm");
|
||||
Cu.import("resource://specialpowers/MockPaymentsUIGlue.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -521,15 +522,19 @@ SpecialPowersAPI.prototype = {
|
|||
},
|
||||
|
||||
get MockFilePicker() {
|
||||
return MockFilePicker
|
||||
return MockFilePicker;
|
||||
},
|
||||
|
||||
get MockColorPicker() {
|
||||
return MockColorPicker
|
||||
return MockColorPicker;
|
||||
},
|
||||
|
||||
get MockPermissionPrompt() {
|
||||
return MockPermissionPrompt
|
||||
return MockPermissionPrompt;
|
||||
},
|
||||
|
||||
get MockPaymentsUIGlue() {
|
||||
return MockPaymentsUIGlue;
|
||||
},
|
||||
|
||||
loadChromeScript: function (url) {
|
||||
|
|
|
@ -9,6 +9,7 @@ specialpowers.jar:
|
|||
modules/MockFilePicker.jsm (content/MockFilePicker.jsm)
|
||||
modules/MockColorPicker.jsm (content/MockColorPicker.jsm)
|
||||
modules/MockPermissionPrompt.jsm (content/MockPermissionPrompt.jsm)
|
||||
modules/MockPaymentsUIGlue.jsm (content/MockPaymentsUIGlue.jsm)
|
||||
modules/Assert.jsm (../modules/Assert.jsm)
|
||||
|
||||
% component {59a52458-13e0-4d93-9d85-a637344f29a1} components/SpecialPowersObserver.js
|
||||
|
|
Загрузка…
Ссылка в новой задаче