зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1878273 - Make `Shift` + right click should work as a right click without `Shift` if contextmenu should forcibly be open r=emilio
We have a pref, `dom.event.contextmenu.shift_suppresses_event`, which is default to make `Shift` + right click forcibly open the context menu without dispatching `contextmenu` event to the web. When user wants to use this feature, they may (or must) assume that it works as a right click (without `Shift`) except whether it overrides `contextmenu` event listener of the web app. Therefore, `Selection` should be collapsed at the click point instead of expanding to the click point. Unfortunately, we need to consider it at `mousedown`, not `mouseup` nor `contextmenu`. Therefore, `Shift` state may mismatch with the actual state at `mouseup` and `mousedown`/`poinerdown` listeners of web apps may assume it will expand selection, but I think that we cannot solve these issues. Differential Revision: https://phabricator.services.mozilla.com/D201054
This commit is contained in:
Родитель
5a2f92686a
Коммит
6678244978
|
@ -380,6 +380,118 @@ SimpleTest.waitForFocus(async () => {
|
|||
})`
|
||||
);
|
||||
|
||||
// If Shift + right click should forcibly open context menu, users may want the click to work as
|
||||
// same as without Shift.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
|
||||
["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
|
||||
["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
|
||||
["dom.event.contextmenu.shift_suppresses_event", true],
|
||||
],
|
||||
});
|
||||
nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
|
||||
getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
|
||||
synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
|
||||
ok(
|
||||
getSelection().isCollapsed,
|
||||
`Selection should be collapsed by a Shift + right click on non-editable node when it does not open context menu (${
|
||||
getRangeDescription(getSelection().getRangeAt(0))
|
||||
})`
|
||||
);
|
||||
is(
|
||||
getSelection().focusNode,
|
||||
nonEditableDiv.querySelector("i").firstChild,
|
||||
`Selection should be collapsed at the click point by a Shift + right click on non-editable node when it does not open context menu (${
|
||||
getRangeDescription(getSelection().getRangeAt(0))
|
||||
})`
|
||||
);
|
||||
|
||||
editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
|
||||
getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
|
||||
synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
|
||||
ok(
|
||||
getSelection().isCollapsed,
|
||||
`Selection should be collapsed by a Shift + right click on editable node when it does not open context menu (${
|
||||
getRangeDescription(getSelection().getRangeAt(0))
|
||||
})`
|
||||
);
|
||||
is(
|
||||
getSelection().focusNode,
|
||||
editableDiv.querySelector("i").firstChild,
|
||||
`Selection should be collapsed at the click point by a Shift + right click on editable node when it does not open context menu (${
|
||||
getRangeDescription(getSelection().getRangeAt(0))
|
||||
})`
|
||||
);
|
||||
|
||||
input.focus();
|
||||
input.setSelectionRange(0, 0);
|
||||
synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
|
||||
ok(
|
||||
input.selectionStart == input.selectionEnd,
|
||||
`Selection in <input> should be collapsed by a Shift + right click when it does not open context menu (got: ${
|
||||
input.selectionStart
|
||||
} - ${input.selectionEnd})`
|
||||
);
|
||||
isnot(
|
||||
input.selectionStart,
|
||||
0,
|
||||
`Selection in <input> should be collapsed at the click point by a Shift + right click when it does not open context menu (got: ${
|
||||
input.selectionStart
|
||||
} - ${input.selectionEnd})`
|
||||
);
|
||||
input.blur();
|
||||
|
||||
// If Shift + right click should open context menu, users may want the click to work as
|
||||
// same as a left click.
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
|
||||
["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
|
||||
["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
|
||||
["dom.event.contextmenu.shift_suppresses_event", false],
|
||||
],
|
||||
});
|
||||
nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
|
||||
getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
|
||||
synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
|
||||
is(
|
||||
getRangeDescription(getSelection().getRangeAt(0)),
|
||||
getRangeDescription({
|
||||
startContainer: nonEditableDiv.querySelector("b").firstChild,
|
||||
startOffset: 0,
|
||||
endContainer: nonEditableDiv.querySelector("i").firstChild,
|
||||
endOffset: getSelection().focusOffset,
|
||||
}),
|
||||
`Selection should be extended by a Shift + right click on non-editable node when it should open context menu`
|
||||
);
|
||||
|
||||
editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
|
||||
getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
|
||||
synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
|
||||
is(
|
||||
getRangeDescription(getSelection().getRangeAt(0)),
|
||||
getRangeDescription({
|
||||
startContainer: editableDiv.querySelector("b").firstChild,
|
||||
startOffset: 0,
|
||||
endContainer: editableDiv.querySelector("i").firstChild,
|
||||
endOffset: getSelection().focusOffset,
|
||||
}),
|
||||
`Selection should be extended by a Shift + right click on editable node when it should open context menu`
|
||||
);
|
||||
|
||||
input.focus();
|
||||
input.setSelectionRange(0, 0);
|
||||
synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
|
||||
isnot(
|
||||
input.selectionStart,
|
||||
input.selectionEnd,
|
||||
`Selection in <input> should be extended by a Shift + right click when it should open context menu (got: ${
|
||||
input.selectionStart
|
||||
} - ${input.selectionEnd})`
|
||||
);
|
||||
input.blur();
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -4735,7 +4735,9 @@ nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (aMouseEvent->mButton == MouseButton::eSecondary &&
|
||||
const bool isSecondaryButton =
|
||||
aMouseEvent->mButton == MouseButton::eSecondary;
|
||||
if (isSecondaryButton &&
|
||||
!MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
|
||||
*frameselection, *aMouseEvent, *offsets.content,
|
||||
// When we collapse selection in nsFrameSelection::TakeFocus,
|
||||
|
@ -4833,7 +4835,14 @@ nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
|
|||
const nsFrameSelection::FocusMode focusMode = [&]() {
|
||||
// If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
|
||||
// mimics the old behaviour.
|
||||
if (aMouseEvent->IsShift()) {
|
||||
const bool isShift =
|
||||
aMouseEvent->IsShift() &&
|
||||
// If Shift + secondary button press shoud open context menu without a
|
||||
// contextmenu event, user wants to open context menu like as a
|
||||
// secondary button press without Shift key.
|
||||
!(isSecondaryButton &&
|
||||
StaticPrefs::dom_event_contextmenu_shift_suppresses_event());
|
||||
if (isShift) {
|
||||
// If clicked in a link when focused content is editable, we should
|
||||
// collapse selection in the link for compatibility with Blink.
|
||||
if (isEditor) {
|
||||
|
|
|
@ -128,6 +128,11 @@ promise_test(async () => {
|
|||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
let contextmenuFired = false;
|
||||
function onContextMenu() {
|
||||
contextmenuFired = true;
|
||||
}
|
||||
document.addEventListener("contextmenu", onContextMenu, {capture: true});
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
|
@ -137,13 +142,26 @@ promise_test(async () => {
|
|||
.pointerUp({button: getButtonType(actions)})
|
||||
.keyUp("\uE008")
|
||||
.send();
|
||||
document.removeEventListener("contextmenu", onContextMenu, {capture: true});
|
||||
|
||||
assert_equals(selection.anchorNode, span1.firstChild,
|
||||
"Selection#anchorNode should keep in the first <span> element");
|
||||
assert_equals(selection.anchorOffset, 2,
|
||||
"Selection#anchorNode should keep at 2 of the first <span> element");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
`Selection#focusNode should be in the second <span> element which was clicked by ${button} button`);
|
||||
if (button != "secondary" || contextmenuFired) {
|
||||
assert_equals(selection.anchorNode, span1.firstChild,
|
||||
"Selection#anchorNode should keep in the first <span> element");
|
||||
assert_equals(selection.anchorOffset, 2,
|
||||
"Selection#anchorNode should keep at 2 of the first <span> element");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
`Selection#focusNode should be in the second <span> element which was clicked by ${button} button`);
|
||||
} else {
|
||||
// Special case for Firefox. Firefox users can forcibly open context menu
|
||||
// with pressing shift key. In this case, users may want the secondary
|
||||
// button click to work as without Shift key press.
|
||||
assert_true(selection.isCollapsed,
|
||||
`Selection should be collapsed after ${button} button click because contextmenu was not opened with Shift key`);
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
`Selection should be collapsed in the second <span> element which was clicked by ${
|
||||
button
|
||||
} button because contextmenu was not opened with Shift key`);
|
||||
}
|
||||
}, `Shift + ${button} click should extend the selection`);
|
||||
|
||||
promise_test(async () => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче