Bug 1917201: Remove cancel button from modal while waiting for user to respond to Windows geolocation dialog r=Gijs a=RyanVM

The cancel button is not really useful here and is probably confusing some
users, since they are pressing it anyway, and it is causing this crash. The
ideal fix would be for the Windows dialog to be modal instead of just immovable,
but it isn't.

Differential Revision: https://phabricator.services.mozilla.com/D225154
This commit is contained in:
David Parks 2024-10-18 21:58:28 +00:00
parent 5f342def31
commit cc058e14a5
4 changed files with 95 additions and 15 deletions

View file

@ -360,20 +360,37 @@ void Geolocation::ReallowWithSystemPermissionOrCancel(
do_GetService("@mozilla.org/prompter;1", &rv);
NS_ENSURE_SUCCESS_VOID(rv);
RefPtr<mozilla::dom::Promise> cancelDialogPromise;
// The dialog should include a cancel button if Gecko is prompting the user
// for system permission. It should have no buttons if the OS will be
// doing the prompting.
bool geckoWillPrompt =
GetLocationOSPermission() ==
geolocation::SystemGeolocationPermissionBehavior::GeckoWillPromptUser;
// This combination of flags removes the default yes and no buttons and adds a
// spinner to the title.
const auto kSpinnerNoButtonFlags = nsIPromptService::BUTTON_TITLE_IS_STRING *
nsIPromptService::BUTTON_POS_0 +
nsIPromptService::BUTTON_TITLE_IS_STRING *
nsIPromptService::BUTTON_POS_1 +
nsIPromptService::SHOW_SPINNER;
// This combination of flags indicates there is only one button labeled
// "Cancel".
const auto kCancelButtonFlags =
nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_0;
RefPtr<mozilla::dom::Promise> tabBlockingDialogPromise;
rv = promptSvc->AsyncConfirmEx(
aBrowsingContext, nsIPromptService::MODAL_TYPE_TAB, title.get(),
message.get(),
nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_0,
nullptr, nullptr, nullptr, nullptr, false, JS::UndefinedHandleValue,
getter_AddRefs(cancelDialogPromise));
geckoWillPrompt ? kCancelButtonFlags : kSpinnerNoButtonFlags, nullptr,
nullptr, nullptr, nullptr, false, JS::UndefinedHandleValue,
getter_AddRefs(tabBlockingDialogPromise));
NS_ENSURE_SUCCESS_VOID(rv);
MOZ_ASSERT(cancelDialogPromise);
MOZ_ASSERT(tabBlockingDialogPromise);
// If the cancel dialog promise is resolved or rejected then the dialog is no
// longer visible so we should stop waiting for permission, whether it was
// granted or not.
cancelDialogPromise->AppendNativeHandler(
// If the tab blocking dialog promise is resolved or rejected then the dialog
// is no longer visible so we should stop waiting for permission, whether it
// was granted or not.
tabBlockingDialogPromise->AppendNativeHandler(
new CancelSystemGeolocationPermissionRequest(permissionRequest));
cancelRequestOnError.release();

View file

@ -64,9 +64,6 @@ CommonDialog.prototype = {
if (this.args.button3Label) {
numButtons++;
}
if (numButtons == 0) {
throw new Error("A dialog with no buttons? Can not haz.");
}
this.numButtons = numButtons;
this.hasInputField = false;
this.iconClass = ["question-icon"];
@ -139,7 +136,9 @@ CommonDialog.prototype = {
this.setLabelForNode(this.ui.button1, this.args.button1Label);
}
break;
case 0:
this.ui.button0.hidden = true;
// fall through
case 1:
this.ui.button1.hidden = true;
break;

View file

@ -204,12 +204,13 @@ function dismissPrompt(ui, action) {
case 2:
ui.button2.click();
break;
case "ESC":
case "ESC": {
// XXX This is assuming tab-modal.
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
EventUtils.synthesizeKey("KEY_Escape", {}, browserWin);
break;
case "pollOK":
}
case "pollOK": {
// Buttons are disabled at the moment, poll until they're reenabled.
// Can't use setInterval here, because the window's in a modal state
// and thus DOM events are suppressed.
@ -221,6 +222,15 @@ function dismissPrompt(ui, action) {
clearInterval(interval);
}, 100);
break;
}
case "abort_dialogs": {
let abortDialogEvent = new ui.prompt.CustomEvent("dialogclosing", {
bubbles: true,
detail: { abort: true },
});
ui.prompt.close(abortDialogEvent);
break;
}
case "none":
break;

View file

@ -749,6 +749,60 @@ async function runTests(util) {
await promptDone;
// =====
info("Starting test: ConfirmEx (no buttons)");
state = {
msg: "This is the confirmEx text.",
title: "TestTitle",
iconClass: "question-icon",
titleHidden: true,
textHidden: true,
passHidden: true,
checkHidden: true,
textValue: "",
passValue: "",
checkMsg: "",
checked: false,
butt0Label: "",
butt1Label: "",
focused: null,
defButton: "button0",
};
if (isOSX) {
// OS X doesn't initially focus the button, but rather the infoBody.
state.focused = "infoBody";
}
action = {
buttonClick: "abort_dialogs",
};
promptDone = handlePrompt(state, action);
// BUTTON_TITLE_IS_STRING with no title removes the button.
flags =
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1 +
Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0;
promptArgs = [
"TestTitle",
"This is the confirmEx text.",
flags,
null,
null,
null,
null,
util.useAsync ? false : {},
];
result = await util.prompt("confirmEx", promptArgs);
is(
util.useAsync ? result.buttonNumClicked : result,
1,
"checked button click num unchanged from default"
);
await promptDone;
// =====
info("Starting test: ConfirmEx (ok/cancel, ok)");
state = {