From ec9fa2ac68f6fb7814c3e8622087828c77effd8d Mon Sep 17 00:00:00 2001 From: Jonathan Guillotte-Blouin Date: Thu, 12 Oct 2017 21:31:03 -0400 Subject: [PATCH] Bug 1383300 - Show payment request total and origin in the dialog. r=MattN MozReview-Commit-ID: 9taFJYmQnBP --HG-- extra : rebase_source : 5c522845fa7b436a6dda1b7c873cd209fbaf798d --- .../payments/content/paymentDialog.css | 11 +++++ .../payments/content/paymentDialog.js | 43 +++++++++++++++---- .../payments/content/paymentDialog.xhtml | 5 ++- .../content/paymentDialogFrameScript.js | 40 ++++++++--------- toolkit/components/payments/docs/index.rst | 9 +++- toolkit/components/payments/jar.mn | 1 + .../components/payments/paymentUIService.js | 25 +++++------ .../payments/res/paymentRequest.css | 20 +++++++++ .../components/payments/res/paymentRequest.js | 28 +++++++++--- .../payments/res/paymentRequest.xhtml | 7 +++ 10 files changed, 133 insertions(+), 56 deletions(-) create mode 100644 toolkit/components/payments/content/paymentDialog.css diff --git a/toolkit/components/payments/content/paymentDialog.css b/toolkit/components/payments/content/paymentDialog.css new file mode 100644 index 000000000000..2b0c4902653b --- /dev/null +++ b/toolkit/components/payments/content/paymentDialog.css @@ -0,0 +1,11 @@ +/* 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/. */ + +body { + margin: 0; +} + +#paymentRequestFrame { + border: none; +} diff --git a/toolkit/components/payments/content/paymentDialog.js b/toolkit/components/payments/content/paymentDialog.js index ddeaca764d4c..f12375bafac7 100644 --- a/toolkit/components/payments/content/paymentDialog.js +++ b/toolkit/components/payments/content/paymentDialog.js @@ -13,24 +13,37 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"] .getService(Ci.nsIPaymentRequestService); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + let PaymentDialog = { componentsLoaded: new Map(), frame: null, mm: null, + request: null, + + init(requestId, frame) { + if (!requestId || typeof(requestId) != "string") { + throw new Error("Invalid PaymentRequest ID"); + } + this.request = paymentSrv.getPaymentRequestById(requestId); + + if (!this.request) { + throw new Error(`PaymentRequest not found: ${requestId}`); + } - init(frame) { this.frame = frame; this.mm = frame.frameLoader.messageManager; this.mm.addMessageListener("paymentContentToChrome", this); this.mm.loadFrameScript("chrome://payments/content/paymentDialogFrameScript.js", true); + this.frame.src = "resource://payments/paymentRequest.xhtml"; }, - createShowResponse({requestId, acceptStatus, methodName = "", data = null, + createShowResponse({acceptStatus, methodName = "", data = null, payerName = "", payerEmail = "", payerPhone = ""}) { let showResponse = this.createComponentInstance(Ci.nsIPaymentShowActionResponse); let methodData = this.createComponentInstance(Ci.nsIGeneralResponseData); - showResponse.init(requestId, + showResponse.init(this.request.requestId, acceptStatus, methodName, methodData, @@ -62,9 +75,8 @@ let PaymentDialog = { return component.createInstance(componentInterface); }, - onPaymentCancel(requestId) { + onPaymentCancel() { const showResponse = this.createShowResponse({ - requestId, acceptStatus: Ci.nsIPaymentActionResponse.PAYMENT_REJECTED, }); paymentSrv.respondPayment(showResponse); @@ -72,18 +84,30 @@ let PaymentDialog = { }, receiveMessage({data}) { - let {messageType, requestId} = data; + let {messageType} = data; switch (messageType) { case "initializeRequest": { + let requestSerialized = JSON.parse(JSON.stringify(this.request)); + + // Manually serialize the nsIPrincipal. + let displayHost = this.request.topLevelPrincipal.URI.displayHost; + requestSerialized.topLevelPrincipal = { + URI: { + displayHost, + }, + }; + this.mm.sendAsyncMessage("paymentChromeToContent", { messageType: "showPaymentRequest", - data: window.arguments[0], + data: { + request: requestSerialized, + }, }); break; } case "paymentCancel": { - this.onPaymentCancel(requestId); + this.onPaymentCancel(); break; } } @@ -91,4 +115,5 @@ let PaymentDialog = { }; let frame = document.getElementById("paymentRequestFrame"); -PaymentDialog.init(frame); +let requestId = (new URLSearchParams(window.location.search)).get("requestId"); +PaymentDialog.init(requestId, frame); diff --git a/toolkit/components/payments/content/paymentDialog.xhtml b/toolkit/components/payments/content/paymentDialog.xhtml index 1e0418691152..aaa7a7b3f9c1 100644 --- a/toolkit/components/payments/content/paymentDialog.xhtml +++ b/toolkit/components/payments/content/paymentDialog.xhtml @@ -6,14 +6,15 @@ + + + name="paymentRequestFrame"> diff --git a/toolkit/components/payments/content/paymentDialogFrameScript.js b/toolkit/components/payments/content/paymentDialogFrameScript.js index cc5aa8ccd18a..13d026fc294d 100644 --- a/toolkit/components/payments/content/paymentDialogFrameScript.js +++ b/toolkit/components/payments/content/paymentDialogFrameScript.js @@ -24,7 +24,14 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); let PaymentFrameScript = { init() { - this.defineLazyLogGetter(this, "frameScript"); + XPCOMUtils.defineLazyGetter(this, "log", () => { + let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); + return new ConsoleAPI({ + maxLogLevelPref: "dom.payments.loglevel", + prefix: "paymentDialogFrameScript", + }); + }); + addEventListener("paymentContentToChrome", this, false, true); addMessageListener("paymentChromeToContent", this); @@ -39,35 +46,22 @@ let PaymentFrameScript = { }, sendToChrome({detail}) { - let {messageType, requestId} = detail; - this.log.debug(`received message from content: ${messageType} ... ${requestId}`); - this.sendMessageToChrome(messageType, { - requestId, - }); - }, - - defineLazyLogGetter(scope, logPrefix) { - XPCOMUtils.defineLazyGetter(scope, "log", () => { - let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); - return new ConsoleAPI({ - maxLogLevelPref: "dom.payments.loglevel", - prefix: logPrefix, - }); - }); + let {messageType} = detail; + this.log.debug("sendToChrome:", messageType, detail); + this.sendMessageToChrome(messageType, detail); }, sendToContent(messageType, detail = {}) { - this.log.debug(`sendToContent (${messageType})`); + this.log.debug("sendToContent", messageType, detail); let response = Object.assign({messageType}, detail); - let event = new content.document.defaultView.CustomEvent("paymentChromeToContent", { - bubbles: true, - detail: Cu.cloneInto(response, content.document.defaultView), + let event = new content.CustomEvent("paymentChromeToContent", { + detail: Cu.cloneInto(response, content), }); - content.document.dispatchEvent(event); + content.dispatchEvent(event); }, - sendMessageToChrome(messageType, detail = {}) { - sendAsyncMessage("paymentContentToChrome", Object.assign(detail, {messageType})); + sendMessageToChrome(messageType, data = {}) { + sendAsyncMessage("paymentContentToChrome", Object.assign(data, {messageType})); }, }; diff --git a/toolkit/components/payments/docs/index.rst b/toolkit/components/payments/docs/index.rst index bbabce052a64..b0584b65b267 100644 --- a/toolkit/components/payments/docs/index.rst +++ b/toolkit/components/payments/docs/index.rst @@ -14,6 +14,13 @@ Debugging Set the pref ``dom.payments.loglevel`` to "Debug". +To open a debugger in the context of the remote payment frame, run the following while the dialog is the most recent window: +`` +gDevToolsBrowser.openContentProcessToolbox({ + selectedBrowser: Services.wm.getMostRecentWindow(null).document.getElementById("paymentRequestFrame").frameLoader, +}) +`` + Communication with the DOM ========================== @@ -32,7 +39,7 @@ 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. +* A ``paymentChromeToContent`` event is dispatched on the ``window`` 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. diff --git a/toolkit/components/payments/jar.mn b/toolkit/components/payments/jar.mn index 8cc75dba0200..93924ebd3d93 100644 --- a/toolkit/components/payments/jar.mn +++ b/toolkit/components/payments/jar.mn @@ -4,6 +4,7 @@ toolkit.jar: % content payments %content/payments/ + content/payments/paymentDialog.css (content/paymentDialog.css) content/payments/paymentDialog.js (content/paymentDialog.js) content/payments/paymentDialogFrameScript.js (content/paymentDialogFrameScript.js) content/payments/paymentDialog.xhtml (content/paymentDialog.xhtml) diff --git a/toolkit/components/payments/paymentUIService.js b/toolkit/components/payments/paymentUIService.js index 44f069099527..0a0103e32449 100644 --- a/toolkit/components/payments/paymentUIService.js +++ b/toolkit/components/payments/paymentUIService.js @@ -25,19 +25,15 @@ XPCOMUtils.defineLazyServiceGetter(this, "@mozilla.org/dom/payments/payment-request-service;1", "nsIPaymentRequestService"); -function defineLazyLogGetter(scope, logPrefix) { - XPCOMUtils.defineLazyGetter(scope, "log", () => { +function PaymentUIService() { + this.wrappedJSObject = this; + XPCOMUtils.defineLazyGetter(this, "log", () => { let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); return new ConsoleAPI({ maxLogLevelPref: "dom.payments.loglevel", - prefix: logPrefix, + prefix: "Payment UI Service", }); }); -} - -function PaymentUIService() { - this.wrappedJSObject = this; - defineLazyLogGetter(this, "Payment UI Service"); this.log.debug("constructor"); } @@ -50,16 +46,15 @@ PaymentUIService.prototype = { // nsIPaymentUIService implementation: showPayment(requestId) { - this.log.debug(`showPayment: ${requestId}`); + this.log.debug("showPayment:", requestId); let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser"); - chromeWindow.openDialog(this.DIALOG_URL, + chromeWindow.openDialog(`${this.DIALOG_URL}?requestId=${requestId}`, `${this.REQUEST_ID_PREFIX}${requestId}`, - "modal,dialog,centerscreen", - {requestId}); + "modal,dialog,centerscreen,resizable=no"); }, abortPayment(requestId) { - this.log.debug(`abortPayment: ${requestId}`); + this.log.debug("abortPayment:", requestId); let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"] .createInstance(Ci.nsIPaymentAbortActionResponse); @@ -84,7 +79,7 @@ PaymentUIService.prototype = { }, completePayment(requestId) { - this.log.debug(`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); @@ -92,7 +87,7 @@ PaymentUIService.prototype = { }, updatePayment(requestId) { - this.log.debug(`updatePayment: ${requestId}`); + this.log.debug("updatePayment:", requestId); }, // other helper methods diff --git a/toolkit/components/payments/res/paymentRequest.css b/toolkit/components/payments/res/paymentRequest.css index b7e0a1d5ef11..dcda5066e302 100644 --- a/toolkit/components/payments/res/paymentRequest.css +++ b/toolkit/components/payments/res/paymentRequest.css @@ -2,3 +2,23 @@ * 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/. */ +html { + background: -moz-dialog; +} + +#total { + border: 1px solid black; + margin: 5px; + text-align: center; +} + +#total .label { + font-size: 15px; + font-weight: bold; +} + +#cancel { + position: absolute; + bottom: 10px; + left: 10px; +} diff --git a/toolkit/components/payments/res/paymentRequest.js b/toolkit/components/payments/res/paymentRequest.js index f3611f74a863..61c3cc613a91 100644 --- a/toolkit/components/payments/res/paymentRequest.js +++ b/toolkit/components/payments/res/paymentRequest.js @@ -11,14 +11,16 @@ "use strict"; let PaymentRequest = { - requestId: null, + request: null, + domReadyPromise: null, init() { // listen to content window.addEventListener("paymentChromeToContent", this); - // listen to user events - window.addEventListener("DOMContentLoaded", this, {once: true}); + this.domReadyPromise = new Promise(function dcl(resolve) { + window.addEventListener("DOMContentLoaded", resolve, {once: true}); + }).then(this.handleEvent.bind(this)); // This scope is now ready to listen to the initialization data this.sendMessageToChrome("initializeRequest"); @@ -57,7 +59,6 @@ let PaymentRequest = { let event = new CustomEvent("paymentContentToChrome", { bubbles: true, detail: Object.assign({ - requestId: this.requestId, messageType, }, detail), }); @@ -65,11 +66,12 @@ let PaymentRequest = { }, onChromeToContent({detail}) { - let {messageType, requestId} = detail; + let {messageType} = detail; switch (messageType) { case "showPaymentRequest": { - this.requestId = requestId; + this.request = detail.request; + this.onShowPaymentRequest(); break; } } @@ -83,6 +85,20 @@ let PaymentRequest = { this.sendMessageToChrome("paymentDialogReady"); }, + async onShowPaymentRequest() { + // Handle getting called before the DOM is ready. + await this.domReadyPromise; + + let hostNameEl = document.getElementById("host-name"); + hostNameEl.textContent = this.request.topLevelPrincipal.URI.displayHost; + + let totalItem = this.request.paymentDetails.totalItem; + let totalEl = document.getElementById("total"); + totalEl.querySelector(".value").textContent = totalItem.amount.value; + totalEl.querySelector(".currency").textContent = totalItem.amount.currency; + totalEl.querySelector(".label").textContent = totalItem.label; + }, + onCancel() { this.sendMessageToChrome("paymentCancel"); }, diff --git a/toolkit/components/payments/res/paymentRequest.xhtml b/toolkit/components/payments/res/paymentRequest.xhtml index f34a56b14c3d..33a3e4a2a3d1 100644 --- a/toolkit/components/payments/res/paymentRequest.xhtml +++ b/toolkit/components/payments/res/paymentRequest.xhtml @@ -10,6 +10,13 @@ +
+ +
+

+ + +