Bug 1760709 - Take directionality into account when handling left/right on radios. r=smaug

We have similar code for range inputs. This matches Blink (but not
WebKit), but I think it's a better default.

The spec seems to leave this up to UAs, so for now landing a
moz-specific WPT. Should be trivial to upstream if we want.

Depends on D141705

Differential Revision: https://phabricator.services.mozilla.com/D141706
This commit is contained in:
Emilio Cobos Álvarez
2022-03-22 11:10:54 +00:00
parent 4975a01d8f
commit 62cddf479c
3 changed files with 111 additions and 35 deletions

View File

@@ -3773,41 +3773,7 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
case eKeyPress: {
if (mType == FormControlType::InputRadio && !keyEvent->IsAlt() &&
!keyEvent->IsControl() && !keyEvent->IsMeta()) {
bool isMovingBack = false;
switch (keyEvent->mKeyCode) {
case NS_VK_UP:
case NS_VK_LEFT:
isMovingBack = true;
[[fallthrough]];
case NS_VK_DOWN:
case NS_VK_RIGHT:
// Arrow key pressed, focus+select prev/next radio button
nsIRadioGroupContainer* container = GetRadioGroupContainer();
if (container) {
nsAutoString name;
GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
RefPtr<HTMLInputElement> selectedRadioButton;
container->GetNextRadioButton(
name, isMovingBack, this,
getter_AddRefs(selectedRadioButton));
if (selectedRadioButton) {
FocusOptions options;
ErrorResult error;
selectedRadioButton->Focus(options, CallerType::System,
error);
rv = error.StealNSResult();
if (NS_SUCCEEDED(rv)) {
rv = DispatchSimulatedClick(selectedRadioButton,
aVisitor.mEvent->IsTrusted(),
aVisitor.mPresContext);
if (NS_SUCCEEDED(rv)) {
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
}
}
}
}
}
rv = MaybeHandleRadioButtonNavigation(aVisitor, keyEvent->mKeyCode);
}
/*
@@ -4100,6 +4066,52 @@ nsresult HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
return MaybeInitPickers(aVisitor);
}
enum class RadioButtonMove { Back, Forward, None };
nsresult HTMLInputElement::MaybeHandleRadioButtonNavigation(
EventChainPostVisitor& aVisitor, uint32_t aKeyCode) {
auto move = [&] {
switch (aKeyCode) {
case NS_VK_UP:
return RadioButtonMove::Back;
case NS_VK_DOWN:
return RadioButtonMove::Forward;
case NS_VK_LEFT:
case NS_VK_RIGHT: {
const bool isRtl = GetComputedDirectionality() == eDir_RTL;
return isRtl == (aKeyCode == NS_VK_LEFT) ? RadioButtonMove::Forward
: RadioButtonMove::Back;
}
}
return RadioButtonMove::None;
}();
if (move == RadioButtonMove::None) {
return NS_OK;
}
// Arrow key pressed, focus+select prev/next radio button
RefPtr<HTMLInputElement> selectedRadioButton;
if (nsIRadioGroupContainer* container = GetRadioGroupContainer()) {
nsAutoString name;
GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
container->GetNextRadioButton(name, move == RadioButtonMove::Back, this,
getter_AddRefs(selectedRadioButton));
}
if (!selectedRadioButton) {
return NS_OK;
}
FocusOptions options;
ErrorResult error;
selectedRadioButton->Focus(options, CallerType::System, error);
if (error.Failed()) {
return error.StealNSResult();
}
nsresult rv = DispatchSimulatedClick(
selectedRadioButton, aVisitor.mEvent->IsTrusted(), aVisitor.mPresContext);
if (NS_SUCCEEDED(rv)) {
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
}
return rv;
}
void HTMLInputElement::PostHandleEventForRangeThumb(
EventChainPostVisitor& aVisitor) {
MOZ_ASSERT(mType == FormControlType::InputRange);