Bug 1458376 - Use a <xul:browser> to display the PaymentRequest UI to fix <select> dropdowns. r=jaws
This fixes <select> dropdowns and also gets rid of the thin bottom gray line in the dialog.
MozReview-Commit-ID: I7v0mVjAnQZ
--- a/browser/components/payments/content/paymentDialogWrapper.js
+++ b/browser/components/payments/content/paymentDialogWrapper.js
@@ -7,16 +7,17 @@
* own scope.
*/
"use strict";
const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
.getService(Ci.nsIPaymentRequestService);
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
@@ -154,17 +155,20 @@ var paymentDialogWrapper = {
if (!this.request) {
throw new Error(`PaymentRequest not found: ${requestId}`);
}
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";
+ if (AppConstants.platform == "win") {
+ this.frame.setAttribute("selectmenulist", "ContentSelectDropdown-windows");
+ }
+ this.frame.loadURI("resource://payments/paymentRequest.xhtml");
},
createShowResponse({
acceptStatus,
methodName = "",
methodData = null,
payerName = "",
payerEmail = "",
rename from browser/components/payments/content/paymentDialogWrapper.xhtml
rename to browser/components/payments/content/paymentDialogWrapper.xul
--- a/browser/components/payments/content/paymentDialogWrapper.xhtml
+++ b/browser/components/payments/content/paymentDialogWrapper.xul
@@ -1,21 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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 xmlns="http://www.w3.org/1999/xhtml">
-<head>
- <title></title>
- <link rel="stylesheet" href="chrome://payments/content/paymentDialogWrapper.css"/>
-</head>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://payments/content/paymentDialogWrapper.css"?>
+
+<!DOCTYPE window>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <popupset>
+ <!-- for select dropdowns. The menupopup is what shows the list of options,
+ and the popuponly menulist makes things like the menuactive attributes
+ work correctly on the menupopup. ContentSelectDropdown expects the
+ popuponly menulist to be its immediate parent. -->
+ <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
+ <menupopup rolluponmousewheel="true"
+ activateontab="true" position="after_start"
+ level="parent"
+ />
+ </menulist>
-<body>
- <iframe type="content"
- id="paymentRequestFrame"
- mozbrowser="true"
- remote="true"
- height="400"
- width="700"></iframe>
- <script src="chrome://payments/content/paymentDialogWrapper.js"></script>
-</body>
-</html>
+ <!-- same as above but with additional attributes for Windows -->
+ <menulist popuponly="true" id="ContentSelectDropdown-windows" hidden="true">
+ <menupopup rolluponmousewheel="true"
+ activateontab="true" position="after_start"
+ level="parent"
+ consumeoutsideclicks="false" ignorekeys="shortcuts"
+ />
+ </menulist>
+ </popupset>
+
+ <browser type="content"
+ id="paymentRequestFrame"
+ disablehistory="true"
+ nodefaultsrc="true"
+ remote="true"
+ selectmenulist="ContentSelectDropdown"
+ style="height:400px;width:700px"
+ transparent="true"></browser>
+ <script type="application/javascript" src="chrome://payments/content/paymentDialogWrapper.js"></script>
+</window>
--- a/browser/components/payments/docs/index.rst
+++ b/browser/components/payments/docs/index.rst
@@ -53,17 +53,17 @@ 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 (paymentDialogWrapper.xhtml) containing a remote ``<iframe mozbrowser="true" remote="true">`` containing unprivileged XHTML (paymentRequest.xhtml).
+Privileged wrapper XUL document (paymentDialogWrapper.xul) containing a remote ``<xul:browser="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 ``window`` with the ``detail`` property populated when the privileged dialog wrapper communicates with the unprivileged dialog.
--- a/browser/components/payments/jar.mn
+++ b/browser/components/payments/jar.mn
@@ -2,17 +2,17 @@
# 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.jar:
% content payments %content/payments/
content/payments/paymentDialogFrameScript.js (content/paymentDialogFrameScript.js)
content/payments/paymentDialogWrapper.css (content/paymentDialogWrapper.css)
content/payments/paymentDialogWrapper.js (content/paymentDialogWrapper.js)
- content/payments/paymentDialogWrapper.xhtml (content/paymentDialogWrapper.xhtml)
+ content/payments/paymentDialogWrapper.xul (content/paymentDialogWrapper.xul)
% resource payments %res/payments/
res/payments (res/paymentRequest.*)
res/payments/components/ (res/components/*.css)
res/payments/components/ (res/components/*.js)
res/payments/containers/ (res/containers/*.js)
res/payments/containers/ (res/containers/*.css)
res/payments/debugging.css (res/debugging.css)
--- a/browser/components/payments/paymentUIService.js
+++ b/browser/components/payments/paymentUIService.js
@@ -33,17 +33,17 @@ function PaymentUIService() {
});
});
this.log.debug("constructor");
}
PaymentUIService.prototype = {
classID: Components.ID("{01f8bd55-9017-438b-85ec-7c15d2b35cdc}"),
QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
- DIALOG_URL: "chrome://payments/content/paymentDialogWrapper.xhtml",
+ DIALOG_URL: "chrome://payments/content/paymentDialogWrapper.xul",
REQUEST_ID_PREFIX: "paymentRequest-",
// nsIPaymentUIService implementation:
showPayment(requestId) {
this.log.debug("showPayment:", requestId);
let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
chromeWindow.openDialog(`${this.DIALOG_URL}?requestId=${requestId}`,
--- a/browser/components/payments/test/browser/browser.ini
+++ b/browser/components/payments/test/browser/browser.ini
@@ -4,16 +4,17 @@ prefs =
dom.payments.request.enabled=true
skip-if = !e10s # Bug 1365964 - Payment Request isn't implemented for non-e10s
support-files =
blank_page.html
[browser_address_edit.js]
[browser_card_edit.js]
[browser_change_shipping.js]
+[browser_dropdowns.js]
[browser_host_name.js]
[browser_payments_onboarding_wizard.js]
[browser_profile_storage.js]
[browser_request_serialization.js]
[browser_request_shipping.js]
[browser_request_summary.js]
uses-unsafe-cpows = true
[browser_shippingaddresschange_error.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/payments/test/browser/browser_dropdowns.js
@@ -0,0 +1,51 @@
+"use strict";
+
+add_task(async function test_dropdown() {
+ await addSampleAddressesAndBasicCard();
+
+ await BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: BLANK_PAGE_URL,
+ }, async browser => {
+ let {win, frame} = await setupPaymentDialog(browser, {
+ details: PTU.Details.total60USD,
+ methodData: [PTU.MethodData.basicCard],
+ merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
+ });
+
+ let popupset = frame.ownerDocument.querySelector("popupset");
+ ok(popupset, "popupset exists");
+ let popupshownPromise = BrowserTestUtils.waitForEvent(popupset, "popupshown");
+
+ info("switch to the address add page");
+ await spawnPaymentDialogTask(frame, async function changeToAddressAddPage() {
+ let {
+ PaymentTestUtils: PTU,
+ } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+ let addLink = content.document.querySelector("address-picker a");
+ is(addLink.textContent, "Add", "Add link text");
+
+ addLink.click();
+
+ await PTU.DialogContentUtils.waitForState(content, (state) => {
+ return state.page.id == "address-page" && !state.page.guid;
+ }, "Check add page state");
+ });
+
+ info("going to open the country <select>");
+ await BrowserTestUtils.synthesizeMouseAtCenter("#country", {}, frame);
+
+ let event = await popupshownPromise;
+ let expectedPopupID = "ContentSelectDropdown";
+ if (AppConstants.platform == "win") {
+ expectedPopupID = "ContentSelectDropdown-windows";
+ }
+ is(event.target.parentElement.id, expectedPopupID, "Checked menulist of opened popup");
+
+ info("clicking cancel");
+ spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
+
+ await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
+ });
+});