зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1435871 - Implement a basic tab-modal dialog container for Payment Request. r=jaws
Differential Revision: https://phabricator.services.mozilla.com/D7934 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1e439596ae
Коммит
72a245b99d
|
@ -1102,8 +1102,13 @@ window._gBrowser = {
|
|||
if (newBrowser.hasAttribute("tabmodalPromptShowing")) {
|
||||
let prompts = newBrowser.parentNode.getElementsByTagNameNS(this._XUL_NS, "tabmodalprompt");
|
||||
let prompt = prompts[prompts.length - 1];
|
||||
prompt.Dialog.setDefaultFocus();
|
||||
return;
|
||||
// @tabmodalPromptShowing is also set for other tab modal prompts
|
||||
// (e.g. the Payment Request dialog) so there may not be a <tabmodalprompt>.
|
||||
// Bug 1492814 will implement this for the Payment Request dialog.
|
||||
if (prompt) {
|
||||
prompt.Dialog.setDefaultFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Focus the location bar if it was previously focused for that tab.
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
|
||||
.getService(Ci.nsIPaymentRequestService);
|
||||
|
||||
const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
|
||||
.getService(Ci.nsIPaymentUIService);
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
@ -216,7 +219,7 @@ var paymentDialogWrapper = {
|
|||
if (AppConstants.platform == "win") {
|
||||
this.frame.setAttribute("selectmenulist", "ContentSelectDropdown-windows");
|
||||
}
|
||||
this.frame.loadURI("resource://payments/paymentRequest.xhtml");
|
||||
this.frame.setAttribute("src", "resource://payments/paymentRequest.xhtml");
|
||||
|
||||
this.temporaryStore = {
|
||||
addresses: new TempCollection("addresses"),
|
||||
|
@ -452,7 +455,7 @@ var paymentDialogWrapper = {
|
|||
Services.obs.addObserver(this, "formautofill-storage-changed", true);
|
||||
|
||||
let requestSerialized = this._serializeRequest(this.request);
|
||||
let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let chromeWindow = window.frameElement.ownerGlobal;
|
||||
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWindow);
|
||||
|
||||
let [savedAddresses, savedBasicCards] =
|
||||
|
@ -474,12 +477,11 @@ var paymentDialogWrapper = {
|
|||
Cu.reportError("devtools.chrome.enabled must be enabled to debug the frame");
|
||||
return;
|
||||
}
|
||||
let chromeWindow = Services.wm.getMostRecentWindow(null);
|
||||
let {
|
||||
gDevToolsBrowser,
|
||||
} = ChromeUtils.import("resource://devtools/client/framework/gDevTools.jsm", {});
|
||||
gDevToolsBrowser.openContentProcessToolbox({
|
||||
selectedBrowser: chromeWindow.document.getElementById("paymentRequestFrame").frameLoader,
|
||||
selectedBrowser: document.getElementById("paymentRequestFrame").frameLoader,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -494,8 +496,9 @@ var paymentDialogWrapper = {
|
|||
const showResponse = this.createShowResponse({
|
||||
acceptStatus: Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
|
||||
});
|
||||
|
||||
paymentSrv.respondPayment(showResponse);
|
||||
window.close();
|
||||
paymentUISrv.closePayment(this.request.requestId);
|
||||
},
|
||||
|
||||
async onPay({
|
||||
|
@ -575,7 +578,7 @@ var paymentDialogWrapper = {
|
|||
|
||||
onCloseDialogMessage() {
|
||||
// The PR is complete(), just close the dialog
|
||||
window.close();
|
||||
paymentUISrv.closePayment(this.request.requestId);
|
||||
},
|
||||
|
||||
async onUpdateAutofillRecord(collectionName, record, guid, messageID) {
|
||||
|
@ -685,6 +688,12 @@ var paymentDialogWrapper = {
|
|||
this.onPaymentCancel();
|
||||
break;
|
||||
}
|
||||
case "paymentDialogReady": {
|
||||
window.dispatchEvent(new Event("tabmodaldialogready", {
|
||||
bubbles: true,
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case "pay": {
|
||||
this.onPay(data);
|
||||
break;
|
||||
|
@ -693,6 +702,9 @@ var paymentDialogWrapper = {
|
|||
this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, data.messageID);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`paymentDialogWrapper: Unexpected messageType: ${messageType}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -15,8 +15,12 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
|
||||
"resource:///modules/BrowserWindowTracker.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"paymentSrv",
|
||||
|
@ -45,10 +49,40 @@ PaymentUIService.prototype = {
|
|||
|
||||
showPayment(requestId) {
|
||||
this.log.debug("showPayment:", requestId);
|
||||
let chromeWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
chromeWindow.openDialog(`${this.DIALOG_URL}?requestId=${requestId}`,
|
||||
`${this.REQUEST_ID_PREFIX}${requestId}`,
|
||||
"modal,dialog,centerscreen,resizable=no");
|
||||
let request = paymentSrv.getPaymentRequestById(requestId);
|
||||
let merchantBrowser = this.findBrowserByTabId(request.tabId);
|
||||
let chromeWindow = merchantBrowser.ownerGlobal;
|
||||
let {gBrowser} = chromeWindow;
|
||||
let browserContainer = gBrowser.getBrowserContainer(merchantBrowser);
|
||||
let container = chromeWindow.document.createElementNS(XHTML_NS, "div");
|
||||
container.dataset.requestId = requestId;
|
||||
container.classList.add("paymentDialogContainer");
|
||||
container.hidden = true;
|
||||
let paymentsBrowser = chromeWindow.document.createElementNS(XHTML_NS, "iframe");
|
||||
paymentsBrowser.classList.add("paymentDialogContainerFrame");
|
||||
paymentsBrowser.setAttribute("type", "content");
|
||||
paymentsBrowser.setAttribute("remote", "true");
|
||||
paymentsBrowser.setAttribute("src", `${this.DIALOG_URL}?requestId=${requestId}`);
|
||||
// append the frame to start the loading
|
||||
container.appendChild(paymentsBrowser);
|
||||
browserContainer.prepend(container);
|
||||
|
||||
// Only show the frame and change the UI when the dialog is ready to show.
|
||||
paymentsBrowser.addEventListener("tabmodaldialogready", function readyToShow() {
|
||||
container.hidden = false;
|
||||
|
||||
// Prevent focusing or interacting with the <browser>.
|
||||
merchantBrowser.setAttribute("tabmodalPromptShowing", "true");
|
||||
|
||||
// Darken the merchant content area.
|
||||
let tabModalBackground = chromeWindow.document.createElement("box");
|
||||
tabModalBackground.classList.add("tab-modal-background", "payment-dialog-background");
|
||||
// Insert the same way as <tabmodalprompt>.
|
||||
merchantBrowser.parentNode.insertBefore(tabModalBackground,
|
||||
merchantBrowser.nextElementSibling);
|
||||
}, {
|
||||
once: true,
|
||||
});
|
||||
},
|
||||
|
||||
abortPayment(requestId) {
|
||||
|
@ -81,6 +115,18 @@ PaymentUIService.prototype = {
|
|||
closed = this.closeDialog(requestId);
|
||||
break;
|
||||
}
|
||||
|
||||
let dialogContainer;
|
||||
if (!closed) {
|
||||
// We need to call findDialog before we respond below as getPaymentRequestById
|
||||
// may fail due to the request being removed upon completion.
|
||||
dialogContainer = this.findDialog(requestId).dialogContainer;
|
||||
if (!dialogContainer) {
|
||||
this.log.error("completePayment: no dialog found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let responseCode = closed ?
|
||||
Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED :
|
||||
Ci.nsIPaymentActionResponse.COMPLETE_FAILED;
|
||||
|
@ -90,23 +136,18 @@ PaymentUIService.prototype = {
|
|||
paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
|
||||
|
||||
if (!closed) {
|
||||
let dialog = this.findDialog(requestId);
|
||||
if (!dialog) {
|
||||
this.log.error("completePayment: no dialog found");
|
||||
return;
|
||||
}
|
||||
dialog.paymentDialogWrapper.updateRequest();
|
||||
dialogContainer.querySelector("iframe").contentWindow.paymentDialogWrapper.updateRequest();
|
||||
}
|
||||
},
|
||||
|
||||
updatePayment(requestId) {
|
||||
let dialog = this.findDialog(requestId);
|
||||
let {dialogContainer} = this.findDialog(requestId);
|
||||
this.log.debug("updatePayment:", requestId);
|
||||
if (!dialog) {
|
||||
if (!dialogContainer) {
|
||||
this.log.error("updatePayment: no dialog found");
|
||||
return;
|
||||
}
|
||||
dialog.paymentDialogWrapper.updateRequest();
|
||||
dialogContainer.querySelector("iframe").contentWindow.paymentDialogWrapper.updateRequest();
|
||||
},
|
||||
|
||||
closePayment(requestId) {
|
||||
|
@ -120,32 +161,48 @@ PaymentUIService.prototype = {
|
|||
* @returns {boolean} whether the specified dialog was closed.
|
||||
*/
|
||||
closeDialog(requestId) {
|
||||
let win = this.findDialog(requestId);
|
||||
if (!win) {
|
||||
let {
|
||||
browser,
|
||||
dialogContainer,
|
||||
} = this.findDialog(requestId);
|
||||
if (!dialogContainer) {
|
||||
return false;
|
||||
}
|
||||
this.log.debug(`closing: ${win.name}`);
|
||||
win.close();
|
||||
this.log.debug(`closing: ${requestId}`);
|
||||
dialogContainer.remove();
|
||||
browser.parentElement.querySelector(".payment-dialog-background").remove();
|
||||
return true;
|
||||
},
|
||||
|
||||
findDialog(requestId) {
|
||||
for (let win of Services.wm.getEnumerator(null)) {
|
||||
if (win.name == `${this.REQUEST_ID_PREFIX}${requestId}`) {
|
||||
return win;
|
||||
for (let win of BrowserWindowTracker.orderedWindows) {
|
||||
for (let dialogContainer of win.document.querySelectorAll(".paymentDialogContainer")) {
|
||||
if (dialogContainer.dataset.requestId == requestId) {
|
||||
return {
|
||||
dialogContainer,
|
||||
browser: dialogContainer.parentElement.querySelector("browser"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
findBrowserByTabId(tabId) {
|
||||
for (let win of BrowserWindowTracker.orderedWindows) {
|
||||
for (let browser of win.gBrowser.browsers) {
|
||||
if (!browser.frameLoader || !browser.frameLoader.tabParent) {
|
||||
continue;
|
||||
}
|
||||
if (browser.frameLoader.tabParent.tabId == tabId) {
|
||||
return browser;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.log.error("findBrowserByTabId: No browser found for tabId:", tabId);
|
||||
return null;
|
||||
},
|
||||
|
||||
requestIdForWindow(window) {
|
||||
let windowName = window.name;
|
||||
|
||||
return windowName.startsWith(this.REQUEST_ID_PREFIX) ?
|
||||
windowName.replace(this.REQUEST_ID_PREFIX, "") : // returns suffix, which is the requestId
|
||||
null;
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentUIService]);
|
||||
|
|
|
@ -109,7 +109,6 @@ var paymentRequest = {
|
|||
onPaymentRequestLoad() {
|
||||
log.debug("onPaymentRequestLoad");
|
||||
window.addEventListener("unload", this, {once: true});
|
||||
this.sendMessageToChrome("paymentDialogReady");
|
||||
|
||||
// Automatically show the debugging console if loaded with a truthy `debug` query parameter.
|
||||
if (new URLSearchParams(location.search).get("debug")) {
|
||||
|
@ -170,6 +169,8 @@ var paymentRequest = {
|
|||
}
|
||||
|
||||
paymentDialog.setStateFromParent(state);
|
||||
|
||||
this.sendMessageToChrome("paymentDialogReady");
|
||||
},
|
||||
|
||||
openPreferences() {
|
||||
|
|
|
@ -112,6 +112,7 @@ var PaymentTestUtils = {
|
|||
* @param {PaymentMethodData[]} methodData
|
||||
* @param {PaymentDetailsInit} details
|
||||
* @param {PaymentOptions} options
|
||||
* @returns {Object}
|
||||
*/
|
||||
createAndShowRequest: ({methodData, details, options}) => {
|
||||
const rq = new content.PaymentRequest(Cu.cloneInto(methodData, content), details, options);
|
||||
|
@ -121,6 +122,9 @@ var PaymentTestUtils = {
|
|||
content.showPromise = rq.show();
|
||||
|
||||
handle.destruct();
|
||||
return {
|
||||
requestId: rq.id,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ add_task(async function test_dropdown() {
|
|||
}
|
||||
is(event.target.parentElement.id, expectedPopupID, "Checked menulist of opened popup");
|
||||
|
||||
event.target.hidePopup(true);
|
||||
|
||||
info("clicking cancel");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
|
||||
|
||||
|
|
|
@ -251,3 +251,68 @@ add_task(async function test_supportedNetworks() {
|
|||
await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_tab_modal() {
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: BLANK_PAGE_URL,
|
||||
}, async browser => {
|
||||
let {win, frame} = await setupPaymentDialog(browser, {
|
||||
methodData,
|
||||
details,
|
||||
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
|
||||
});
|
||||
|
||||
await TestUtils.waitForCondition(() => {
|
||||
return !document.querySelector(".paymentDialogContainer").hidden;
|
||||
}, "Waiting for container to be visible after the dialog's ready");
|
||||
|
||||
ok(!EventUtils.isHidden(win.frameElement), "Frame should be visible");
|
||||
|
||||
let {
|
||||
bottom: toolboxBottom,
|
||||
} = document.getElementById("navigator-toolbox").getBoundingClientRect();
|
||||
|
||||
let {x, y} = win.frameElement.getBoundingClientRect();
|
||||
ok(y > 0, "Frame should have y > 0");
|
||||
// Inset by 10px since the corner point doesn't return the frame due to the
|
||||
// border-radius.
|
||||
is(document.elementFromPoint(x + 10, y + 10), win.frameElement,
|
||||
"Check .paymentDialogContainerFrame is visible");
|
||||
|
||||
info("Click to the left of the dialog over the content area");
|
||||
isnot(document.elementFromPoint(x - 10, y + 50), browser,
|
||||
"Check clicks on the merchant content area don't go to the browser");
|
||||
is(document.elementFromPoint(x - 10, y + 50),
|
||||
document.querySelector(".payment-dialog-background"),
|
||||
"Check clicks on the merchant content area go to the payment dialog background");
|
||||
|
||||
ok(y < toolboxBottom - 2, "Dialog should overlap the toolbox by at least 2px");
|
||||
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: BLANK_PAGE_URL,
|
||||
}, async newBrowser => {
|
||||
let {
|
||||
x: x2,
|
||||
y: y2,
|
||||
} = win.frameElement.getBoundingClientRect();
|
||||
is(x2, x, "Check x-coordinate is the same");
|
||||
is(y2, y, "Check y-coordinate is the same");
|
||||
isnot(document.elementFromPoint(x + 10, y + 10), win.frameElement,
|
||||
"Check .paymentDialogContainerFrame is hidden");
|
||||
});
|
||||
|
||||
let {
|
||||
x: x3,
|
||||
y: y3,
|
||||
} = win.frameElement.getBoundingClientRect();
|
||||
is(x3, x, "Check x-coordinate is the same again");
|
||||
is(y3, y, "Check y-coordinate is the same again");
|
||||
is(document.elementFromPoint(x + 10, y + 10), win.frameElement,
|
||||
"Check .paymentDialogContainerFrame is visible again");
|
||||
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
|
||||
await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,12 +17,13 @@ const SAVE_ADDRESS_DEFAULT_PREF = "dom.payments.defaults.saveAddress";
|
|||
const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"]
|
||||
.getService(Ci.nsIPaymentRequestService);
|
||||
const paymentUISrv = Cc["@mozilla.org/dom/payments/payment-ui-service;1"]
|
||||
.getService().wrappedJSObject;
|
||||
.getService(Ci.nsIPaymentUIService).wrappedJSObject;
|
||||
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
|
||||
const {formAutofillStorage} = ChromeUtils.import(
|
||||
"resource://formautofill/FormAutofillStorage.jsm", {});
|
||||
const {PaymentTestUtils: PTU} = ChromeUtils.import(
|
||||
"resource://testing-common/PaymentTestUtils.jsm", {});
|
||||
ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
function getPaymentRequests() {
|
||||
|
@ -31,19 +32,23 @@ function getPaymentRequests() {
|
|||
|
||||
/**
|
||||
* Return the container (e.g. dialog or overlay) that the payment request contents are shown in.
|
||||
* This abstracts away the details of the widget used so that this can more earily transition from a
|
||||
* dialog to another kind of overlay.
|
||||
* Consumers shouldn't rely on a dialog window being returned.
|
||||
* This abstracts away the details of the widget used so that this can more easily transition to
|
||||
* another kind of dialog/overlay.
|
||||
* @param {string} requestId
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function getPaymentWidget() {
|
||||
let win;
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
win = Services.wm.getMostRecentWindow(null);
|
||||
return win.name.startsWith(paymentUISrv.REQUEST_ID_PREFIX);
|
||||
}, "payment dialog should be the most recent");
|
||||
|
||||
return win;
|
||||
async function getPaymentWidget(requestId) {
|
||||
return BrowserTestUtils.waitForCondition(() => {
|
||||
let {dialogContainer} = paymentUISrv.findDialog(requestId);
|
||||
if (!dialogContainer) {
|
||||
return false;
|
||||
}
|
||||
let browserIFrame = dialogContainer.querySelector("iframe");
|
||||
if (!browserIFrame) {
|
||||
return false;
|
||||
}
|
||||
return browserIFrame.contentWindow;
|
||||
}, "payment dialog should be opened");
|
||||
}
|
||||
|
||||
async function getPaymentFrame(widget) {
|
||||
|
@ -237,19 +242,18 @@ function checkPaymentMethodDetailsMatchesCard(methodDetails, card, msg) {
|
|||
*/
|
||||
async function setupPaymentDialog(browser, {methodData, details, options, merchantTaskFn}) {
|
||||
let dialogReadyPromise = waitForWidgetReady();
|
||||
await ContentTask.spawn(browser,
|
||||
{
|
||||
methodData,
|
||||
details,
|
||||
options,
|
||||
},
|
||||
merchantTaskFn);
|
||||
let {requestId} = await ContentTask.spawn(browser,
|
||||
{
|
||||
methodData,
|
||||
details,
|
||||
options,
|
||||
},
|
||||
merchantTaskFn);
|
||||
ok(requestId, "requestId should be defined");
|
||||
|
||||
// get a reference to the UI dialog and the requestId
|
||||
let [win] = await Promise.all([getPaymentWidget(), dialogReadyPromise]);
|
||||
let [win] = await Promise.all([getPaymentWidget(requestId), dialogReadyPromise]);
|
||||
ok(win, "Got payment widget");
|
||||
let requestId = paymentUISrv.requestIdForWindow(win);
|
||||
ok(requestId, "requestId should be defined");
|
||||
is(win.closed, false, "dialog should not be closed");
|
||||
|
||||
let frame = await getPaymentFrame(win);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Tab Modal Prompt boxes */
|
||||
.tab-modal-background,
|
||||
tabmodalprompt {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -33,3 +34,19 @@ tabmodalprompt {
|
|||
tabmodalprompt label[value=""] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
/* Tab-Modal Payment Request widget */
|
||||
.paymentDialogContainer {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.paymentDialogContainerFrame {
|
||||
box-sizing: border-box;
|
||||
height: 500px;
|
||||
/* Center the dialog with 20% on the left/right and 60% width for content */
|
||||
left: 20%;
|
||||
position: absolute;
|
||||
/* Overlap the chrome */
|
||||
top: -6px;
|
||||
width: 60%;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Tab Modal Prompt boxes */
|
||||
.tab-modal-background,
|
||||
tabmodalprompt {
|
||||
background-color: hsla(0,0%,10%,.5);
|
||||
}
|
||||
|
||||
tabmodalprompt {
|
||||
font-family: sans-serif; /* use content font not system UI font */
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.paymentDialogContainerFrame,
|
||||
.tabmodalprompt-mainContainer {
|
||||
color: black;
|
||||
background-color: hsla(0,0%,100%,.95);
|
||||
|
|
|
@ -3,11 +3,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Tab Modal Prompt boxes */
|
||||
.tab-modal-background,
|
||||
tabmodalprompt {
|
||||
background-color: hsla(0,0%,10%,.5);
|
||||
}
|
||||
|
||||
tabmodalprompt {
|
||||
font-family: sans-serif; /* use content font not system UI font */
|
||||
}
|
||||
|
||||
.paymentDialogContainerFrame,
|
||||
.tabmodalprompt-mainContainer {
|
||||
color: -moz-fieldText;
|
||||
background-color: -moz-field;
|
||||
|
|
Загрузка…
Ссылка в новой задаче