Bug 898499 - Support mozPay API on desktop webapprt. r=ferjm
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Fri, 27 Sep 2013 14:38:14 -0400
changeset 149071 44558c02904e66370dc5a5e28d1b395751de3fa1
parent 149070 54a7972a9358a52229ff00eedab8a13f558255dc
child 149072 ca0f0d8b8cd6dec1c822b47052d62b0011dca40f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersferjm
bugs898499
milestone27.0a1
Bug 898499 - Support mozPay API on desktop webapprt. r=ferjm
b2g/app/b2g.js
browser/confvars.sh
browser/installer/package-manifest.in
dom/payment/Payment.js
mobile/android/app/mobile.js
webapprt/PaymentUIGlue.js
webapprt/Startup.jsm
webapprt/components.manifest
webapprt/locales/en-US/webapprt/webapp.properties
webapprt/moz.build
webapprt/prefs.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -447,16 +447,19 @@ pref("dom.mozPermissionSettings.enabled"
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
 // TCPSocket
 pref("dom.mozTCPSocket.enabled", true);
 
+// WebPayment
+pref("dom.mozPay.enabled", true);
+
 // "Preview" landing of bug 710563, which is bogged down in analysis
 // of talos regression.  This is a needed change for higher-framerate
 // CSS animations, and incidentally works around an apparent bug in
 // our handling of requestAnimationFrame() listeners, which are
 // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior.  The
 // secondary bug isn't really worth investigating since it's obseleted
 // by bug 710563.
 pref("layout.frame_rate.precise", true);
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -53,8 +53,10 @@ MOZ_PROFILE_MIGRATOR=1
 MOZ_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
 MOZ_WEBAPP_RUNTIME=1
 MOZ_MEDIA_NAVIGATOR=1
 if test "$OS_TARGET" = "WINNT" -o "$OS_TARGET" = "Darwin"; then
   MOZ_FOLD_LIBS=1
 fi
 MOZ_WEBGL_CONFORMANT=1
+# Enable navigator.mozPay
+MOZ_PAY=1
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -218,16 +218,17 @@
 @BINPATH@/components/dom_webspeechrecognition.xpt
 #endif
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
 #ifdef MOZ_GAMEPAD
 @BINPATH@/components/dom_gamepad.xpt
 #endif
+@BINPATH@/components/dom_payment.xpt
 @BINPATH@/components/downloads.xpt
 @BINPATH@/components/editor.xpt
 @BINPATH@/components/embed_base.xpt
 @BINPATH@/components/extensions.xpt
 @BINPATH@/components/exthandler.xpt
 @BINPATH@/components/exthelper.xpt
 @BINPATH@/components/fastfind.xpt
 @BINPATH@/components/feeds.xpt
@@ -535,16 +536,21 @@
 @BINPATH@/components/TCPSocket.js
 @BINPATH@/components/TCPServerSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 @BINPATH@/components/AppProtocolHandler.js
 @BINPATH@/components/AppProtocolHandler.manifest
 
+@BINPATH@/components/Payment.js
+@BINPATH@/components/PaymentFlowInfo.js
+@BINPATH@/components/PaymentRequestInfo.js
+@BINPATH@/components/Payment.manifest
+
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/PeerConnection.js
 @BINPATH@/components/PeerConnection.manifest
 #endif
 
 @BINPATH@/components/HttpDataUsage.manifest
 @BINPATH@/components/HttpDataUsage.js
 
@@ -767,16 +773,17 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/webapprt/chrome.manifest
 @BINPATH@/webapprt/chrome/webapprt@JAREXT@
 @BINPATH@/webapprt/chrome/webapprt.manifest
 @BINPATH@/webapprt/chrome/@AB_CD@@JAREXT@
 @BINPATH@/webapprt/chrome/@AB_CD@.manifest
 @BINPATH@/webapprt/components/CommandLineHandler.js
 @BINPATH@/webapprt/components/ContentPermission.js
 @BINPATH@/webapprt/components/DirectoryProvider.js
+@BINPATH@/webapprt/components/PaymentUIGlue.js
 @BINPATH@/webapprt/components/components.manifest
 @BINPATH@/webapprt/defaults/preferences/prefs.js
 @BINPATH@/webapprt/modules/Startup.jsm
 @BINPATH@/webapprt/modules/WebappRT.jsm
 @BINPATH@/webapprt/modules/WebappsHandler.jsm
 @BINPATH@/webapprt/modules/RemoteDebugger.jsm
 #endif
 
--- a/dom/payment/Payment.js
+++ b/dom/payment/Payment.js
@@ -73,16 +73,24 @@ PaymentContentHelper.prototype = {
       requestId: requestId
     });
     return request;
   },
 
   // nsIDOMGlobalPropertyInitializer
 
   init: function(aWindow) {
+    try {
+      if (!Services.prefs.getBoolPref("dom.mozPay.enabled")) {
+        return null;
+      }
+    } catch (e) {
+      return null;
+    }
+
     this._window = aWindow;
     this.initDOMRequestHelper(aWindow, PAYMENT_IPC_MSG_NAMES);
 
     try {
       this._debug =
         Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
         && Services.prefs.getBoolPref(PREF_DEBUG);
     } catch(e) {
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -745,16 +745,19 @@ pref("browser.contentHandlers.types.1.ur
 pref("browser.contentHandlers.types.1.type", "application/vnd.mozilla.maybe.feed");
 pref("browser.contentHandlers.types.2.title", "chrome://browser/locale/region.properties");
 pref("browser.contentHandlers.types.2.uri", "chrome://browser/locale/region.properties");
 pref("browser.contentHandlers.types.2.type", "application/vnd.mozilla.maybe.feed");
 pref("browser.contentHandlers.types.3.title", "chrome://browser/locale/region.properties");
 pref("browser.contentHandlers.types.3.uri", "chrome://browser/locale/region.properties");
 pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
 
+// WebPayment
+pref("dom.mozPay.enabled", true);
+
 #ifndef RELEASE_BUILD
 pref("dom.payment.provider.0.name", "Firefox Marketplace");
 pref("dom.payment.provider.0.description", "marketplace.firefox.com");
 pref("dom.payment.provider.0.uri", "https://marketplace.firefox.com/mozpay/?req=");
 pref("dom.payment.provider.0.type", "mozilla/payments/pay/v1");
 pref("dom.payment.provider.0.requestMethod", "GET");
 #endif
 
new file mode 100644
--- /dev/null
+++ b/webapprt/PaymentUIGlue.js
@@ -0,0 +1,140 @@
+/* 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 { interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                  "@mozilla.org/childprocessmessagemanager;1",
+                                  "nsIMessageSender");
+
+function paymentSuccess(aRequestId) {
+  return paymentCallback(aRequestId, "Payment:Success");
+}
+
+function paymentFailed(aRequestId) {
+  return paymentCallback(aRequestId, "Payment:Failed");
+}
+
+function paymentCallback(aRequestId, aMsg) {
+  return function(aResult) {
+          closePaymentWindow(aRequestId, function() {
+            cpmm.sendAsyncMessage(aMsg, { result: aResult,
+                                          requestId: aRequestId });
+          });
+        };
+}
+
+let payments = {};
+
+function closePaymentWindow(aId, aCallback) {
+  if (payments[aId]) {
+    payments[aId].handled = true;
+    payments[aId].win.close();
+    payments[aId] = null;
+  }
+
+  aCallback();
+}
+
+function PaymentUI() {}
+
+PaymentUI.prototype = {
+  classID: Components.ID("{ede1124f-72e8-4a31-9567-3270d46f21fb}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIGlue]),
+
+  confirmPaymentRequest: function(aRequestId, aRequests, aSuccessCb, aErrorCb) {
+    // If there's only one payment provider that will work, just move on
+    // without prompting the user.
+    if (aRequests.length == 1) {
+      aSuccessCb.onresult(aRequestId, aRequests[0].wrappedJSObject.type);
+      return;
+    }
+
+    let items = [];
+
+    // Otherwise, let the user select a payment provider from a list.
+    for (let i = 0; i < aRequests.length; i++) {
+      let request = aRequests[i].wrappedJSObject;
+      let requestText = request.providerName;
+      if (request.productPrice && Array.isArray(request.productPrice)) {
+        // We should guess the user currency and use that instead.
+        requestText += " (" + request.productPrice[0].amount + " " +
+                              request.productPrice[0].currency + ")";
+      }
+      items.push(requestText);
+    }
+
+    let selected = {};
+
+    let bundle = Services.strings.
+                   createBundle("chrome://webapprt/locale/webapp.properties");
+    let result = Services.prompt.
+                   select(null, bundle.GetStringFromName("paymentDialog.title"),
+                          bundle.GetStringFromName("paymentDialog.message"),
+                          items.length, items, selected);
+    if (result) {
+      aSuccessCb.onresult(aRequestId,
+                          aRequests[selected.value].wrappedJSObject.type);
+    } else {
+      aErrorCb.onresult(aRequestId, "USER_CANCELLED");
+    }
+  },
+
+  showPaymentFlow: function(aRequestId, aPaymentFlowInfo, aErrorCb) {
+    let win = Services.ww.
+                openWindow(null,
+                           "chrome://webapprt/content/webapp.xul",
+                           "_blank",
+                           "chrome,dialog=no,resizable,scrollbars,centerscreen",
+                           null);
+
+    // Store a reference to the window so that we can close it when the payment
+    // succeeds or fails.
+    payments[aRequestId] = { win: win, handled: false };
+
+    // Inject paymentSuccess and paymentFailed methods into the document after
+    // its loaded.
+    win.addEventListener("DOMWindowCreated", function() {
+      let browserElement = win.document.getElementById("content");
+      browserElement.
+        setAttribute("src", aPaymentFlowInfo.uri + aPaymentFlowInfo.jwt);
+
+      browserElement.addEventListener("DOMWindowCreated", function() {
+        win.document.getElementById("content").contentDocument.defaultView
+           .wrappedJSObject.mozPaymentProvider = {
+             __exposedProps__: {
+               paymentSuccess: 'r',
+               paymentFailed: 'r'
+             },
+             paymentSuccess: paymentSuccess(aRequestId),
+             paymentFailed: paymentFailed(aRequestId)
+           };
+      }, true);
+    });
+
+    let winObserver = function(aClosedWin, aTopic) {
+      if (aTopic == "domwindowclosed") {
+        // Fail the payment if the window is closed.
+        if (aClosedWin == win) {
+          Services.ww.unregisterNotification(winObserver);
+          if (!payments[aRequestId].handled) {
+            aErrorCb.onresult(aRequestId, "USER_CANCELLED");
+          }
+        }
+      }
+    }
+
+    Services.ww.registerNotification(winObserver);
+  },
+
+  cleanup: function() {
+  },
+}
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUI]);
--- a/webapprt/Startup.jsm
+++ b/webapprt/Startup.jsm
@@ -12,16 +12,17 @@ this.EXPORTED_SYMBOLS = ["startup"];
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 // Initialize DOMApplicationRegistry by importing Webapps.jsm.
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import('resource://gre/modules/Payment.jsm');
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 // Initialize window-independent handling of webapps- notifications.
 Cu.import("resource://webapprt/modules/WebappsHandler.jsm");
 Cu.import("resource://webapprt/modules/WebappRT.jsm");
 
 function isFirstRunOrUpdate() {
--- a/webapprt/components.manifest
+++ b/webapprt/components.manifest
@@ -6,8 +6,12 @@ category command-line-handler x-default 
 # ContentPermission.js
 component {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4} ContentPermission.js
 contract @mozilla.org/content-permission/prompt;1 {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4}
 
 # DirectoryProvider.js
 component {e1799fda-4b2f-4457-b671-e0641d95698d} DirectoryProvider.js
 contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d}
 category xpcom-directory-providers webapprt-directory-provider @mozilla.org/webapprt/directory-provider;1
+
+# PaymentUIGlue.js
+component {ede1124f-72e8-4a31-9567-3270d46f21fb} PaymentUIGlue.js
+contract @mozilla.org/payment/ui-glue;1 {ede1124f-72e8-4a31-9567-3270d46f21fb}
--- a/webapprt/locales/en-US/webapprt/webapp.properties
+++ b/webapprt/locales/en-US/webapprt/webapp.properties
@@ -35,8 +35,11 @@ desktop-notification.remember=Remember m
 # LOCALIZATION NOTE (webapps.install.title): %S will be replaced with the name
 # of the webapp being installed.
 webapps.install.title=Install %S
 # LOCALIZATION NOTE (webapps.install.description): %S will be replaced with the
 # name of the webapp being installed.
 webapps.install.description=Do you want to install %S?
 webapps.install.install=Install App
 webapps.install.dontinstall=Don't Install
+
+paymentDialog.title=Payment
+paymentDialog.message=Which payment provider do you want to use?
--- a/webapprt/moz.build
+++ b/webapprt/moz.build
@@ -13,16 +13,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gt
 
 DIRS += ['locales']
 TEST_DIRS += ['test']
 
 EXTRA_COMPONENTS += [
     'CommandLineHandler.js',
     'ContentPermission.js',
     'DirectoryProvider.js',
+    'PaymentUIGlue.js',
     'components.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'RemoteDebugger.jsm',
     'Startup.jsm',
     'WebappRT.jsm',
     'WebappsHandler.jsm',
--- a/webapprt/prefs.js
+++ b/webapprt/prefs.js
@@ -41,16 +41,28 @@ pref("browser.cache.offline.enable", tru
 pref("offline-apps.allow_by_default", true);
 
 // TCPSocket
 pref("dom.mozTCPSocket.enabled", true);
 
 // Enable smooth scrolling
 pref("general.smoothScroll", true);
 
+// WebPayment
+pref("dom.mozPay.enabled", true);
+
+#ifndef RELEASE_BUILD
+// Enable mozPay default provider
+pref("dom.payment.provider.0.name", "Firefox Marketplace");
+pref("dom.payment.provider.0.description", "marketplace.firefox.com");
+pref("dom.payment.provider.0.uri", "https://marketplace.firefox.com/mozpay/?req=");
+pref("dom.payment.provider.0.type", "mozilla/payments/pay/v1");
+pref("dom.payment.provider.0.requestMethod", "GET");
+#endif
+
 // Enable window resize and move
 pref("dom.always_allow_move_resize_window", true);
 
 pref("plugin.allowed_types", "application/x-shockwave-flash,application/futuresplash");
 
 pref("devtools.debugger.remote-enabled", true);
 pref("devtools.debugger.force-local", true);