Bug 1402210 - Add payment UI documentation and do minor code cleanup. r=marcosc draft
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Thu, 21 Sep 2017 21:35:24 -0700
changeset 668861 d65efac187f631e0097c300bf0e8fa6f9a78c540
parent 668565 84366b50903805361a536a0ea81ec696820ac7dc
child 668920 d9e6201f163338983211fbed3f250db8c03508e6
child 668921 1fb5ab33cd63a999dd8b28c5dc964f8ad4ac8f3e
push id81144
push usermozilla@noorenberghe.ca
push dateFri, 22 Sep 2017 04:35:46 +0000
reviewersmarcosc
bugs1402210
milestone57.0a1
Bug 1402210 - Add payment UI documentation and do minor code cleanup. r=marcosc MozReview-Commit-ID: Cpn5mKcEZGo
toolkit/components/payments/content/paymentDialog.js
toolkit/components/payments/content/paymentDialogFrameScript.js
toolkit/components/payments/docs/index.rst
toolkit/components/payments/moz.build
toolkit/components/payments/paymentUIService.js
toolkit/components/payments/res/paymentRequest.js
--- a/toolkit/components/payments/content/paymentDialog.js
+++ b/toolkit/components/payments/content/paymentDialog.js
@@ -1,12 +1,17 @@
 /* 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/. */
 
+/**
+ * Runs in the privileged outer dialog. Each dialog loads this script in its
+ * own scope.
+ */
+
 "use strict";
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
                      .getService(Ci.nsIPaymentRequestService);
 
 let PaymentDialog = {
   componentsLoaded: new Map(),
--- a/toolkit/components/payments/content/paymentDialogFrameScript.js
+++ b/toolkit/components/payments/content/paymentDialogFrameScript.js
@@ -1,37 +1,42 @@
 /* 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 frame script only exists to mediate communications between the
+ * unprivileged frame in a content process and the privileged dialog wrapper
+ * in the UI process on the main thread.
+ *
+ * `paymentChromeToContent` messages from the privileged wrapper are converted
+ * into DOM events of the same name.
+ * `paymentContentToChrome` custom DOM events from the unprivileged frame are
+ * converted into messages of the same name.
+ *
+ * Business logic should stay out of this shim.
+ */
+
 "use strict";
 
 /* eslint-env mozilla/frame-script */
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let PaymentFrameScript = {
   init() {
     this.defineLazyLogGetter(this, "frameScript");
     addEventListener("paymentContentToChrome", this, false, true);
 
     addMessageListener("paymentChromeToContent", this);
   },
 
   handleEvent(event) {
-    switch (event.type) {
-      case "paymentContentToChrome": {
-        this.sendToChrome(event);
-        break;
-      }
-      default: {
-        throw new Error("Unexpected event type");
-      }
-    }
+    this.sendToChrome(event);
   },
 
   receiveMessage({data: {messageType, data}}) {
     this.sendToContent(messageType, data);
   },
 
   sendToChrome({detail}) {
     let {messageType, requestId} = detail;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/docs/index.rst
@@ -0,0 +1,38 @@
+==============
+WebPayments UI
+==============
+
+User Interface for the WebPayments `Payment Request API <https://w3c.github.io/browser-payment-api/>`_ and `Payment Handler API <https://w3c.github.io/payment-handler/>`_.
+
+JSDoc style comments are used within the JS files of the component. This document will focus on higher-level and shared concepts.
+
+.. toctree::
+   :maxdepth: 5
+
+Debugging
+=========
+
+Set the pref ``dom.payments.loglevel`` to "Debug".
+
+
+Communication with the DOM
+==========================
+
+Communication from the DOM to the UI happens via the `paymentUIService.js` (implementing ``nsIPaymentUIService``).
+The UI talks to the DOM code via the ``nsIPaymentRequestService`` interface.
+
+
+Dialog Architecture
+===================
+
+Privileged wrapper XHTML document (paymentDialog.xhtml) containing a remote ``<iframe mozbrowser="true" remote="true">`` containing unprivileged XHTML (paymentRequest.xhtml).
+Keeping the dialog contents unprivileged is useful since the dialog will render payment line items and shipping options that are provided by web developers and should therefore be considered untrusted.
+In order to communicate across the process boundary a privileged frame script (`paymentDialogFrameScript.js`) is loaded into the iframe to relay messages.
+This is because the unprivileged document cannot access message managers.
+Instead, all communication across the privileged/unprivileged boundary is done via custom DOM events:
+
+* A ``paymentContentToChrome`` event is dispatched when the dialog contents want to communicate with the privileged dialog wrapper.
+* A ``paymentChromeToContent`` event is dispatched on the ``document`` with the ``detail`` property populated when the privileged dialog wrapper communicates with the unprivileged dialog.
+
+These events are converted to/from message manager messages of the same name to communicate to the other process.
+The purpose of `paymentDialogFrameScript.js` is to simply convert unprivileged DOM events to/from messages from the other process.
--- a/toolkit/components/payments/moz.build
+++ b/toolkit/components/payments/moz.build
@@ -1,16 +1,21 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 
+with Files('**'):
+    BUG_COMPONENT = ('Toolkit', 'WebPayments UI')
+
 EXTRA_COMPONENTS += [
     'payments.manifest',
     'paymentUIService.js',
 ]
 
 EXTRA_JS_MODULES += []
 
 JAR_MANIFESTS += ['jar.mn']
+
+SPHINX_TREES['docs'] = 'docs'
--- a/toolkit/components/payments/paymentUIService.js
+++ b/toolkit/components/payments/paymentUIService.js
@@ -1,17 +1,26 @@
 /* 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/. */
 
+/**
+ * Singleton service acting as glue between the DOM APIs and the payment dialog UI.
+ *
+ * Communication from the DOM to the UI happens via the nsIPaymentUIService interface.
+ * The UI talks to the DOM code via the nsIPaymentRequestService interface.
+ * PaymentUIService is started by the DOM code lazily.
+ *
+ * For now the UI is shown in a native dialog but that is likely to change.
+ * Tests should try to avoid relying on that implementation detail.
+ */
+
 "use strict";
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
-// eslint-disable-next-line no-unused-vars
-const DIALOG_URL = "chrome://payments/content/paymentDialog.xhtml";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "paymentSrv",
                                    "@mozilla.org/dom/payments/payment-request-service;1",
                                    "nsIPaymentRequestService");
@@ -30,22 +39,25 @@ function PaymentUIService() {
   this.wrappedJSObject = this;
   defineLazyLogGetter(this, "Payment UI Service");
   this.log.debug("constructor");
 }
 
 PaymentUIService.prototype = {
   classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+  DIALOG_URL: "chrome://payments/content/paymentDialog.xhtml",
   REQUEST_ID_PREFIX: "paymentRequest-",
 
+  // nsIPaymentUIService implementation:
+
   showPayment(requestId) {
-    this.log.debug("showPayment");
+    this.log.debug(`showPayment: ${requestId}`);
     let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
-    chromeWindow.openDialog(DIALOG_URL,
+    chromeWindow.openDialog(this.DIALOG_URL,
                             `${this.REQUEST_ID_PREFIX}${requestId}`,
                             "modal,dialog,centerscreen",
                             {requestId});
   },
 
   abortPayment(requestId) {
     this.log.debug(`abortPayment: ${requestId}`);
     let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"]
@@ -67,23 +79,25 @@ PaymentUIService.prototype = {
       Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED :
       Ci.nsIPaymentActionResponse.ABORT_FAILED;
 
     abortResponse.init(requestId, response);
     paymentSrv.respondPayment(abortResponse);
   },
 
   completePayment(requestId) {
+    this.log.debug(`completePayment: ${requestId}`);
     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"]
                              .createInstance(Ci.nsIPaymentCompleteActionResponse);
     completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLTETE_SUCCEEDED);
     paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
   },
 
   updatePayment(requestId) {
+    this.log.debug(`updatePayment: ${requestId}`);
   },
 
   // other helper methods
 
   requestIdForWindow(window) {
     let windowName = window.name;
 
     return windowName.startsWith(this.REQUEST_ID_PREFIX) ?
--- a/toolkit/components/payments/res/paymentRequest.js
+++ b/toolkit/components/payments/res/paymentRequest.js
@@ -1,12 +1,18 @@
 /* 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/. */
 
+/**
+ * Loaded in the unprivileged frame of each payment dialog.
+ *
+ * Communicates with privileged code via DOM Events.
+ */
+
 "use strict";
 
 let PaymentRequest = {
   requestId: null,
 
   init() {
     // listen to content
     window.addEventListener("paymentChromeToContent", this);