mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1691622 - part 4: Make synthesizeNativeMouseClick*()
aware of modifiers r=smaug,geckoview-reviewers,m_kato
Surprisingly, they don't take modifiers, and `nsIWidget::SynthesizeNativeMouseEvent()` which are implementations of `nsIDOMWindowUtils::SendNativeMouseEvent()` treat given modifier flags are native's ones, and handle modifiers only on macOS. Therefore, this patch makes them handle native modifiers of Gecko. Unfortunately, I'm not so familiar with Android API, and in the short term, I don't need the support on Android. Therefore, this patch just adds a TODO comment on Android widget. Additionally, we don't have a simple way to set modifier only while posting a mouse input on Windows too. It requires complicated code. Therefore, I don't add the support for it on Windows too. Differential Revision: https://phabricator.services.mozilla.com/D105758
This commit is contained in:
parent
613d02b00b
commit
b63882dde8
@ -935,7 +935,9 @@ struct RoleDescrComparator {
|
||||
LayoutDeviceIntPoint(geckoRect.X() + (geckoRect.Width() / 2),
|
||||
geckoRect.Y() + (geckoRect.Height() / 2));
|
||||
nsIWidget* widget = [objOrView widget];
|
||||
widget->SynthesizeNativeMouseEvent(p, NSEventTypeRightMouseDown, 0, nullptr);
|
||||
widget->SynthesizeNativeMouseEvent(p, NSEventTypeRightMouseDown,
|
||||
nsIWidget::Modifiers::NO_MODIFIERS,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
- (void)moxPerformPress {
|
||||
|
@ -920,9 +920,96 @@ nsresult nsDOMWindowUtils::SendTouchEventCommon(
|
||||
return rv;
|
||||
}
|
||||
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::CAPS_LOCK) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_CAPS_LOCK),
|
||||
"Need to sync CapsLock value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::NUM_LOCK) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_NUM_LOCK),
|
||||
"Need to sync NumLock value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::SHIFT_L) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_SHIFT_LEFT),
|
||||
"Need to sync ShiftLeft value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::SHIFT_R) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_SHIFT_RIGHT),
|
||||
"Need to sync ShiftRight value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::CTRL_L) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_CONTROL_LEFT),
|
||||
"Need to sync ControlLeft value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::CTRL_R) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_CONTROL_RIGHT),
|
||||
"Need to sync ControlRight value between nsIWidget::Modifiers "
|
||||
"and nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::ALT_L) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_LEFT),
|
||||
"Need to sync AltLeft value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::ALT_R) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_RIGHT),
|
||||
"Need to sync AltRight value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::COMMAND_L) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_COMMAND_LEFT),
|
||||
"Need to sync CommandLeft value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::COMMAND_R) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_COMMAND_RIGHT),
|
||||
"Need to sync CommandRight value between nsIWidget::Modifiers "
|
||||
"and nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::HELP) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_HELP),
|
||||
"Need to sync Help value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::ALTGRAPH) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_GRAPH),
|
||||
"Need to sync AltGraph value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(
|
||||
static_cast<uint32_t>(nsIWidget::Modifiers::FUNCTION) ==
|
||||
static_cast<uint32_t>(nsIDOMWindowUtils::NATIVE_MODIFIER_FUNCTION),
|
||||
"Need to sync Function value between nsIWidget::Modifiers and "
|
||||
"nsIDOMWindowUtils");
|
||||
static_assert(static_cast<uint32_t>(nsIWidget::Modifiers::NUMERIC_KEY_PAD) ==
|
||||
static_cast<uint32_t>(
|
||||
nsIDOMWindowUtils::NATIVE_MODIFIER_NUMERIC_KEY_PAD),
|
||||
"Need to sync NumericKeyPad value between nsIWidget::Modifiers "
|
||||
"and nsIDOMWindowUtils");
|
||||
|
||||
static nsIWidget::Modifiers GetWidgetModifiers(uint32_t aNativeModifiers) {
|
||||
nsIWidget::Modifiers widgetModifiers = static_cast<nsIWidget::Modifiers>(
|
||||
aNativeModifiers &
|
||||
(nsIWidget::Modifiers::CAPS_LOCK | nsIWidget::Modifiers::NUM_LOCK |
|
||||
nsIWidget::Modifiers::SHIFT_L | nsIWidget::Modifiers::SHIFT_R |
|
||||
nsIWidget::Modifiers::CTRL_L | nsIWidget::Modifiers::CTRL_R |
|
||||
nsIWidget::Modifiers::ALT_L | nsIWidget::Modifiers::ALT_R |
|
||||
nsIWidget::Modifiers::COMMAND_L | nsIWidget::Modifiers::COMMAND_R |
|
||||
nsIWidget::Modifiers::HELP | nsIWidget::Modifiers::ALTGRAPH |
|
||||
nsIWidget::Modifiers::FUNCTION | nsIWidget::Modifiers::NUMERIC_KEY_PAD));
|
||||
NS_ASSERTION(static_cast<uint32_t>(widgetModifiers) == aNativeModifiers,
|
||||
"Invalid value is specified to the native modifiers");
|
||||
return widgetModifiers;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
|
||||
int32_t aNativeKeyCode, int32_t aModifiers,
|
||||
int32_t aNativeKeyCode,
|
||||
uint32_t aModifiers,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters,
|
||||
nsIObserver* aObserver) {
|
||||
@ -935,15 +1022,15 @@ nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout,
|
||||
nsIObserver*>(
|
||||
"nsIWidget::SynthesizeNativeKeyEvent", widget,
|
||||
&nsIWidget::SynthesizeNativeKeyEvent, aNativeKeyboardLayout,
|
||||
aNativeKeyCode, aModifiers, aCharacters, aUnmodifiedCharacters,
|
||||
aObserver)));
|
||||
aNativeKeyCode, static_cast<uint32_t>(GetWidgetModifiers(aModifiers)),
|
||||
aCharacters, aUnmodifiedCharacters, aObserver)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX, int32_t aScreenY,
|
||||
int32_t aNativeMessage,
|
||||
int32_t aModifierFlags,
|
||||
uint32_t aModifierFlags,
|
||||
Element* aElement,
|
||||
nsIObserver* aObserver) {
|
||||
// get the widget to send the event to
|
||||
@ -951,11 +1038,12 @@ nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX, int32_t aScreenY,
|
||||
if (!widget) return NS_ERROR_FAILURE;
|
||||
|
||||
NS_DispatchToMainThread(NativeInputRunnable::Create(
|
||||
NewRunnableMethod<LayoutDeviceIntPoint, int32_t, int32_t, nsIObserver*>(
|
||||
NewRunnableMethod<LayoutDeviceIntPoint, int32_t, nsIWidget::Modifiers,
|
||||
nsIObserver*>(
|
||||
"nsIWidget::SynthesizeNativeMouseEvent", widget,
|
||||
&nsIWidget::SynthesizeNativeMouseEvent,
|
||||
LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage,
|
||||
aModifierFlags, aObserver)));
|
||||
GetWidgetModifiers(aModifierFlags), aObserver)));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -475,6 +475,30 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
in long aLineOrPageDeltaY,
|
||||
in unsigned long aOptions);
|
||||
|
||||
/**
|
||||
* Native modifiers for sendNativeKeyEvent and sendNativeMouseEvent.
|
||||
* TODO: The other sendNative*Event should take these values instead.
|
||||
*/
|
||||
const unsigned long NATIVE_MODIFIER_CAPS_LOCK = 0x00000001;
|
||||
const unsigned long NATIVE_MODIFIER_NUM_LOCK = 0x00000002;
|
||||
const unsigned long NATIVE_MODIFIER_SHIFT_LEFT = 0x00000100;
|
||||
const unsigned long NATIVE_MODIFIER_SHIFT_RIGHT = 0x00000200;
|
||||
const unsigned long NATIVE_MODIFIER_CONTROL_LEFT = 0x00000400;
|
||||
const unsigned long NATIVE_MODIFIER_CONTROL_RIGHT = 0x00000800;
|
||||
const unsigned long NATIVE_MODIFIER_ALT_LEFT = 0x00001000;
|
||||
const unsigned long NATIVE_MODIFIER_ALT_RIGHT = 0x00002000;
|
||||
const unsigned long NATIVE_MODIFIER_COMMAND_LEFT = 0x00004000;
|
||||
const unsigned long NATIVE_MODIFIER_COMMAND_RIGHT = 0x00008000;
|
||||
const unsigned long NATIVE_MODIFIER_HELP = 0x00010000;
|
||||
// On Windows, AltGraph key emulates the AltRight key on specific keyboard
|
||||
// layouts. Therefore, this shouldn't be used without `synthesizeNativeKey`.
|
||||
const unsigned long NATIVE_MODIFIER_ALT_GRAPH = 0x00020000;
|
||||
// Available only on macOS.
|
||||
const unsigned long NATIVE_MODIFIER_FUNCTION = 0x00100000;
|
||||
// Available only on macOS. When pressing a key in numeric key pad, this
|
||||
// must be included.
|
||||
const unsigned long NATIVE_MODIFIER_NUMERIC_KEY_PAD = 0x01000000;
|
||||
|
||||
/**
|
||||
* See nsIWidget::SynthesizeNativeKeyEvent
|
||||
*
|
||||
@ -489,7 +513,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
void sendNativeKeyEvent(in long aNativeKeyboardLayout,
|
||||
in long aNativeKeyCode,
|
||||
in long aModifierFlags,
|
||||
in unsigned long aModifierFlags,
|
||||
in AString aCharacters,
|
||||
in AString aUnmodifiedCharacters,
|
||||
[optional] in nsIObserver aObserver);
|
||||
@ -508,7 +532,7 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
void sendNativeMouseEvent(in long aScreenX,
|
||||
in long aScreenY,
|
||||
in long aNativeMessage,
|
||||
in long aModifierFlags,
|
||||
in unsigned long aModifierFlags,
|
||||
in Element aElement,
|
||||
[optional] in nsIObserver aObserver);
|
||||
/**
|
||||
|
@ -1784,8 +1784,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseEvent(
|
||||
AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent");
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (widget) {
|
||||
widget->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, aModifierFlags,
|
||||
responder.GetObserver());
|
||||
widget->SynthesizeNativeMouseEvent(
|
||||
aPoint, aNativeMessage,
|
||||
static_cast<nsIWidget::Modifiers>(aModifierFlags),
|
||||
responder.GetObserver());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
@ -148,6 +148,71 @@ function nativeMouseUpEventMsg() {
|
||||
);
|
||||
}
|
||||
|
||||
function parseNativeModifiers(aModifiers, aWindow = window) {
|
||||
let modifiers = 0;
|
||||
if (aModifiers.capsLockKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CAPS_LOCK;
|
||||
}
|
||||
if (aModifiers.numLockKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_NUM_LOCK;
|
||||
}
|
||||
if (aModifiers.shiftKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_SHIFT_LEFT;
|
||||
}
|
||||
if (aModifiers.shiftRightKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_SHIFT_RIGHT;
|
||||
}
|
||||
if (aModifiers.ctrlKey) {
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_LEFT;
|
||||
}
|
||||
if (aModifiers.ctrlRightKey) {
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_RIGHT;
|
||||
}
|
||||
if (aModifiers.altKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_LEFT;
|
||||
}
|
||||
if (aModifiers.altRightKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_RIGHT;
|
||||
}
|
||||
if (aModifiers.metaKey) {
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_LEFT;
|
||||
}
|
||||
if (aModifiers.metaRightKey) {
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_RIGHT;
|
||||
}
|
||||
if (aModifiers.helpKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_HELP;
|
||||
}
|
||||
if (aModifiers.fnKey) {
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_FUNCTION;
|
||||
}
|
||||
if (aModifiers.numericKeyPadKey) {
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_NUMERIC_KEY_PAD;
|
||||
}
|
||||
|
||||
if (aModifiers.accelKey) {
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_LEFT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_LEFT;
|
||||
}
|
||||
if (aModifiers.accelRightKey) {
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_RIGHT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_RIGHT;
|
||||
}
|
||||
if (aModifiers.altGrKey) {
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_LEFT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_GRAPH;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
function getBoundingClientRectRelativeToVisualViewport(aElement) {
|
||||
let utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
var rect = aElement.getBoundingClientRect();
|
||||
@ -750,6 +815,7 @@ function synthesizeNativeMouseClickWithAPZ(aParams, aObserver = null) {
|
||||
atCenter, // Instead of offsetX/Y, synthesize the event at center of `target`
|
||||
screenX, // X offset in screen, offsetX/Y nor atCenter must not be set if this is set
|
||||
screenY, // Y offset in screen, offsetX/Y nor atCenter must not be set if this is set
|
||||
modifiers = {}, // Active modifiers, see `parseNativeModifiers`
|
||||
} = aParams;
|
||||
if (atCenter) {
|
||||
if (offsetX != undefined || offsetY != undefined) {
|
||||
@ -789,18 +855,19 @@ function synthesizeNativeMouseClickWithAPZ(aParams, aObserver = null) {
|
||||
const utils = SpecialPowers.getDOMWindowUtils(
|
||||
target.ownerDocument.defaultView
|
||||
);
|
||||
const modifierFlags = parseNativeModifiers(modifiers);
|
||||
utils.sendNativeMouseEvent(
|
||||
pt.x,
|
||||
pt.y,
|
||||
nativeMouseDownEventMsg(),
|
||||
0,
|
||||
modifierFlags,
|
||||
target,
|
||||
function() {
|
||||
utils.sendNativeMouseEvent(
|
||||
pt.x,
|
||||
pt.y,
|
||||
nativeMouseUpEventMsg(),
|
||||
0,
|
||||
modifierFlags,
|
||||
target,
|
||||
aObserver
|
||||
);
|
||||
|
@ -34,6 +34,8 @@ const kStrictKeyPressEvents =
|
||||
SpecialPowers.getBoolPref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content");
|
||||
const kStrictKeyDownKeyUpEvents =
|
||||
SpecialPowers.getBoolPref("dom.keyboardevent.dispatch_during_composition");
|
||||
const kIsHeadless =
|
||||
SpecialPowers.Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).isHeadless;
|
||||
|
||||
info("\nProfile::EventUtilsLoadTime: " + (loadTime - start) + "\n");
|
||||
function starttest() {
|
||||
@ -51,6 +53,145 @@ function starttest() {
|
||||
sendMouseEvent({type:'click'}, "testMouseEvent");
|
||||
is(check, true, 'sendMouseEvent should dispatch click event');
|
||||
|
||||
await (async function testSynthesizeNativeMouseClick() {
|
||||
if (navigator.appVersion.includes("Android")) {
|
||||
todo(false,
|
||||
`testSynthesizeNativeMouseClick: does nothing on Android because its nsIWidget::SynthesizeNativeMouseEvent won't fire "mouseup"`);
|
||||
return;
|
||||
}
|
||||
let events = [];
|
||||
let listener = event => events.push(event);
|
||||
let preventDefault = event => event.preventDefault();
|
||||
$("testMouseEvent").addEventListener("mousedown", listener);
|
||||
$("testMouseEvent").addEventListener("mouseup", listener);
|
||||
// Clicking with modifiers may open context menu so that we should prevent to open it.
|
||||
window.addEventListener("contextmenu", preventDefault, { capture: true });
|
||||
for (const test of [
|
||||
{
|
||||
description: "ShiftLeft",
|
||||
modifiers: { shiftKey: true },
|
||||
},
|
||||
{
|
||||
description: "ShiftRight",
|
||||
modifiers: { shiftRightKey: true },
|
||||
},
|
||||
{
|
||||
description: "CtrlLeft",
|
||||
modifiers: { ctrlKey: true },
|
||||
},
|
||||
{
|
||||
description: "CtrlRight",
|
||||
modifiers: { ctrlRightKey: true },
|
||||
},
|
||||
{
|
||||
description: "AltLeft",
|
||||
modifiers: { altKey: true },
|
||||
},
|
||||
{
|
||||
description: "AltRight",
|
||||
modifiers: { altRightKey: true },
|
||||
},
|
||||
{
|
||||
description: "MetaLeft",
|
||||
modifiers: { metaKey: true },
|
||||
skip: () => {
|
||||
// We've not supported "Meta" as Windows logo key or Super/Hyper keys.
|
||||
return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "MetaRight",
|
||||
modifiers: { metaRightKey: true },
|
||||
skip: () => {
|
||||
// We've not supported "Meta" as Windows logo key or Super/Hyper keys.
|
||||
return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "CapsLock",
|
||||
modifiers: { capsLockKey: true },
|
||||
},
|
||||
{
|
||||
description: "NumLock",
|
||||
modifiers: { numLockKey: true },
|
||||
skip: () => {
|
||||
// macOS does not have `NumLock` key nor state.
|
||||
return navigator.platform.includes("Mac");
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Ctrl+Shift",
|
||||
modifiers: { ctrlKey: true, shiftKey: true },
|
||||
skip: () => {
|
||||
// We forcibly open context menu on macOS so the following test
|
||||
// will fail to receive mouse events.
|
||||
return navigator.platform.includes("Mac");
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Alt+Shift",
|
||||
modifiers: { altKey: true, shiftKey: true },
|
||||
},
|
||||
{
|
||||
description: "Meta+Shift",
|
||||
modifiers: { metaKey: true, shiftKey: true },
|
||||
skip: () => {
|
||||
// We've not supported "Meta" as Windows logo key or Super/Hyper keys.
|
||||
return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
|
||||
},
|
||||
},
|
||||
]) {
|
||||
if (test.skip && test.skip()) {
|
||||
continue;
|
||||
}
|
||||
events = [];
|
||||
info(`testSynthesizeNativeMouseClick: sending native mouse click (${test.description})`);
|
||||
await promiseNativeMouseClickAndWaitForEvent({
|
||||
target: $("testMouseEvent"),
|
||||
atCenter: true,
|
||||
modifiers: test.modifiers,
|
||||
eventTypeToWait: "mouseup",
|
||||
});
|
||||
is(events.length, 2,
|
||||
`testSynthesizeNativeMouseClick: a pair of "mousedown" and "mouseup" events should be fired (${test.description})`);
|
||||
is(events[0]?.type, "mousedown",
|
||||
`testSynthesizeNativeMouseClick: "mousedown" should be fired (${test.description})`);
|
||||
is(events[1]?.type, "mouseup",
|
||||
`testSynthesizeNativeMouseClick: "mouseup" should be fired (${test.description})`);
|
||||
if (events.length !== 2) {
|
||||
continue;
|
||||
}
|
||||
for (const mod of [{ keyName: "Alt", propNames: [ "altKey", "altRightKey" ]},
|
||||
{ keyName: "Control", propNames: [ "ctrlKey", "ctrlRightKey" ]},
|
||||
{ keyName: "Shift", propNames: [ "shiftKey", "shiftRightKey" ]},
|
||||
{ keyName: "Meta", propNames: [ "metaKey", "metaRightKey" ]},
|
||||
{ keyName: "CapsLock", propNames: [ "capsLockKey" ]},
|
||||
{ keyName: "NumLock", propNames: [ "numLockKey" ]},
|
||||
]) {
|
||||
const activeExpected =
|
||||
(test.modifiers.hasOwnProperty(mod.propNames[0]) &&
|
||||
test.modifiers[mod.propNames[0]]) ||
|
||||
(mod.propNames.length !== 1 &&
|
||||
test.modifiers.hasOwnProperty(mod.propNames[1]) &&
|
||||
test.modifiers[mod.propNames[1]]);
|
||||
const checkFn = activeExpected && (
|
||||
// Bug 1693240: We don't support setting modifiers while posting a mouse event on Windows.
|
||||
navigator.platform.includes("Win") ||
|
||||
// Bug 1693237: We don't support setting modifiers on Android.
|
||||
navigator.appVersion.includes("Android") ||
|
||||
// In Headless mode, modifiers are not supported by this kind of APIs.
|
||||
kIsHeadless) ? todo_is : is;
|
||||
checkFn(events[0]?.getModifierState(mod.keyName), activeExpected,
|
||||
`testSynthesizeNativeMouseCheck: "mousedown".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
|
||||
checkFn(events[1]?.getModifierState(mod.keyName), activeExpected,
|
||||
`testSynthesizeNativeMouseCheck: "mouseup".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
|
||||
}
|
||||
}
|
||||
$("testMouseEvent").removeEventListener("mousedown", listener);
|
||||
$("testMouseEvent").removeEventListener("mouseup", listener);
|
||||
window.removeEventListener("contextmenu", preventDefault, { capture: true });
|
||||
})();
|
||||
|
||||
check = false;
|
||||
$("testKeyEvent").addEventListener("keypress", doCheck, {once: true});
|
||||
$("testKeyEvent").focus();
|
||||
|
@ -1061,6 +1061,7 @@ function synthesizeNativeMouseClick(aParams, aCallback = null) {
|
||||
atCenter, // Instead of offsetX/Y, synthesize the event at center of `target`
|
||||
screenX, // X offset in screen, offsetX/Y nor atCenter must not be set if this is set
|
||||
screenY, // Y offset in screen, offsetX/Y nor atCenter must not be set if this is set
|
||||
modifiers = {}, // Active modifiers, see `_parseNativeModifiers`
|
||||
win = window, // The window to use its utils
|
||||
} = aParams;
|
||||
if (atCenter) {
|
||||
@ -1117,6 +1118,7 @@ function synthesizeNativeMouseClick(aParams, aCallback = null) {
|
||||
scale
|
||||
);
|
||||
})();
|
||||
const modifierFlags = _parseNativeModifiers(modifiers);
|
||||
|
||||
const observer = {
|
||||
observe: (subject, topic, data) => {
|
||||
@ -1129,14 +1131,14 @@ function synthesizeNativeMouseClick(aParams, aCallback = null) {
|
||||
x,
|
||||
y,
|
||||
_EU_nativeMouseDownEventMsg(),
|
||||
0,
|
||||
modifierFlags,
|
||||
null,
|
||||
function() {
|
||||
utils.sendNativeMouseEvent(
|
||||
x,
|
||||
y,
|
||||
_EU_nativeMouseUpEventMsg(),
|
||||
0,
|
||||
modifierFlags,
|
||||
null,
|
||||
observer
|
||||
);
|
||||
@ -1349,55 +1351,66 @@ function synthesizeAndWaitKey(
|
||||
}
|
||||
|
||||
function _parseNativeModifiers(aModifiers, aWindow = window) {
|
||||
var modifiers;
|
||||
let modifiers = 0;
|
||||
if (aModifiers.capsLockKey) {
|
||||
modifiers |= 0x00000001;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CAPS_LOCK;
|
||||
}
|
||||
if (aModifiers.numLockKey) {
|
||||
modifiers |= 0x00000002;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_NUM_LOCK;
|
||||
}
|
||||
if (aModifiers.shiftKey) {
|
||||
modifiers |= 0x00000100;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_SHIFT_LEFT;
|
||||
}
|
||||
if (aModifiers.shiftRightKey) {
|
||||
modifiers |= 0x00000200;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_SHIFT_RIGHT;
|
||||
}
|
||||
if (aModifiers.ctrlKey) {
|
||||
modifiers |= 0x00000400;
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_LEFT;
|
||||
}
|
||||
if (aModifiers.ctrlRightKey) {
|
||||
modifiers |= 0x00000800;
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_RIGHT;
|
||||
}
|
||||
if (aModifiers.altKey) {
|
||||
modifiers |= 0x00001000;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_LEFT;
|
||||
}
|
||||
if (aModifiers.altRightKey) {
|
||||
modifiers |= 0x00002000;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_RIGHT;
|
||||
}
|
||||
if (aModifiers.metaKey) {
|
||||
modifiers |= 0x00004000;
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_LEFT;
|
||||
}
|
||||
if (aModifiers.metaRightKey) {
|
||||
modifiers |= 0x00008000;
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_RIGHT;
|
||||
}
|
||||
if (aModifiers.helpKey) {
|
||||
modifiers |= 0x00010000;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_HELP;
|
||||
}
|
||||
if (aModifiers.fnKey) {
|
||||
modifiers |= 0x00100000;
|
||||
modifiers |= SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_FUNCTION;
|
||||
}
|
||||
if (aModifiers.numericKeyPadKey) {
|
||||
modifiers |= 0x01000000;
|
||||
modifiers |=
|
||||
SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_NUMERIC_KEY_PAD;
|
||||
}
|
||||
|
||||
if (aModifiers.accelKey) {
|
||||
modifiers |= _EU_isMac(aWindow) ? 0x00004000 : 0x00000400;
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_LEFT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_LEFT;
|
||||
}
|
||||
if (aModifiers.accelRightKey) {
|
||||
modifiers |= _EU_isMac(aWindow) ? 0x00008000 : 0x00000800;
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_COMMAND_RIGHT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_CONTROL_RIGHT;
|
||||
}
|
||||
if (aModifiers.altGrKey) {
|
||||
modifiers |= _EU_isWin(aWindow) ? 0x00020000 : 0x00001000;
|
||||
modifiers |= _EU_isMac(aWindow)
|
||||
? SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_LEFT
|
||||
: SpecialPowers.Ci.nsIDOMWindowUtils.NATIVE_MODIFIER_ALT_GRAPH;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
@ -428,13 +428,14 @@ nsresult PuppetWidget::SynthesizeNativeKeyEvent(
|
||||
|
||||
nsresult PuppetWidget::SynthesizeNativeMouseEvent(
|
||||
mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags, nsIObserver* aObserver) {
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) {
|
||||
AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
if (!mBrowserChild) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mBrowserChild->SendSynthesizeNativeMouseEvent(
|
||||
aPoint, aNativeMessage, aModifierFlags, notifier.SaveObserver());
|
||||
aPoint, aNativeMessage, static_cast<uint32_t>(aModifierFlags),
|
||||
notifier.SaveObserver());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -251,10 +251,9 @@ class PuppetWidget : public nsBaseWidget,
|
||||
int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
|
||||
uint32_t aModifierFlags, const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseScrollEvent(
|
||||
|
@ -2438,10 +2438,9 @@ nsresult nsWindow::SynthesizeNativeTouchPoint(uint32_t aPointerId,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) {
|
||||
mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
|
||||
MOZ_ASSERT(mNPZCSupport.IsAttached());
|
||||
@ -2453,6 +2452,7 @@ nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
aPoint.x -= bounds.x;
|
||||
aPoint.y -= bounds.y;
|
||||
|
||||
// TODO (bug 1693237): Handle aModifierFlags.
|
||||
DispatchToUiThread(
|
||||
"nsWindow::SynthesizeNativeMouseEvent",
|
||||
[npzc = java::PanZoomController::NativeProvider::GlobalRef(npzc),
|
||||
|
@ -202,7 +202,7 @@ class nsWindow final : public nsBaseWidget {
|
||||
nsIObserver* aObserver) override;
|
||||
nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIWidget::Modifiers aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override;
|
||||
|
@ -4725,27 +4725,8 @@ nsresult TextInputHandlerBase::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardL
|
||||
const nsAString& aUnmodifiedCharacters) {
|
||||
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
|
||||
|
||||
static const uint32_t sModifierFlagMap[][2] = {
|
||||
{nsIWidget::CAPS_LOCK, NSEventModifierFlagCapsLock},
|
||||
{nsIWidget::SHIFT_L, NSEventModifierFlagShift | 0x0002},
|
||||
{nsIWidget::SHIFT_R, NSEventModifierFlagShift | 0x0004},
|
||||
{nsIWidget::CTRL_L, NSEventModifierFlagControl | 0x0001},
|
||||
{nsIWidget::CTRL_R, NSEventModifierFlagControl | 0x2000},
|
||||
{nsIWidget::ALT_L, NSEventModifierFlagOption | 0x0020},
|
||||
{nsIWidget::ALT_R, NSEventModifierFlagOption | 0x0040},
|
||||
{nsIWidget::COMMAND_L, NSEventModifierFlagCommand | 0x0008},
|
||||
{nsIWidget::COMMAND_R, NSEventModifierFlagCommand | 0x0010},
|
||||
{nsIWidget::NUMERIC_KEY_PAD, NSEventModifierFlagNumericPad},
|
||||
{nsIWidget::HELP, NSEventModifierFlagHelp},
|
||||
{nsIWidget::FUNCTION, NSEventModifierFlagFunction}};
|
||||
|
||||
uint32_t modifierFlags = 0;
|
||||
for (uint32_t i = 0; i < ArrayLength(sModifierFlagMap); ++i) {
|
||||
if (aModifierFlags & sModifierFlagMap[i][0]) {
|
||||
modifierFlags |= sModifierFlagMap[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t modifierFlags = nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(
|
||||
static_cast<nsIWidget::Modifiers>(aModifierFlags));
|
||||
NSInteger windowNumber = [[mView window] windowNumber];
|
||||
bool sendFlagsChangedEvent = IsModifierKey(aNativeKeyCode);
|
||||
NSEventType eventType = sendFlagsChangedEvent ? NSEventTypeFlagsChanged : NSEventTypeKeyDown;
|
||||
|
@ -387,12 +387,13 @@ class nsChildView final : public nsBaseWidget {
|
||||
nsIObserver* aObserver) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIWidget::Modifiers aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override {
|
||||
return SynthesizeNativeMouseEvent(aPoint, NSEventTypeMouseMoved, 0, aObserver);
|
||||
return SynthesizeNativeMouseEvent(aPoint, NSEventTypeMouseMoved,
|
||||
nsIWidget::Modifiers::NO_MODIFIERS, aObserver);
|
||||
}
|
||||
virtual nsresult SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage, double aDeltaX,
|
||||
|
@ -868,7 +868,8 @@ nsresult nsChildView::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
|
||||
}
|
||||
|
||||
nsresult nsChildView::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage, uint32_t aModifierFlags,
|
||||
uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
|
||||
|
||||
@ -884,10 +885,12 @@ nsresult nsChildView::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
// expects a point in a coordinate system that has its origin on the bottom left.
|
||||
NSPoint screenPoint = NSMakePoint(pt.x, nsCocoaUtils::FlippedScreenY(pt.y));
|
||||
NSPoint windowPoint = nsCocoaUtils::ConvertPointFromScreen([mView window], screenPoint);
|
||||
NSEventModifierFlags modifierFlags =
|
||||
nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(aModifierFlags);
|
||||
|
||||
NSEvent* event = [NSEvent mouseEventWithType:(NSEventType)aNativeMessage
|
||||
location:windowPoint
|
||||
modifierFlags:aModifierFlags
|
||||
modifierFlags:modifierFlags
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:[[mView window] windowNumber]
|
||||
context:nil
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
// Declare the backingScaleFactor method that we want to call
|
||||
// on NSView/Window/Screen objects, if they recognize it.
|
||||
@ -382,6 +383,13 @@ class nsCocoaUtils {
|
||||
*/
|
||||
static uint32_t ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode);
|
||||
|
||||
/**
|
||||
* Converts Gecko native modifier flags for `nsIWidget::SynthesizeNative*()`
|
||||
* to native modifier flags of macOS.
|
||||
*/
|
||||
static NSEventModifierFlags ConvertWidgetModifiersToMacModifierFlags(
|
||||
nsIWidget::Modifiers aNativeModifiers);
|
||||
|
||||
/**
|
||||
* Convert string with font attribute to NSMutableAttributedString
|
||||
*/
|
||||
|
@ -1040,6 +1040,38 @@ uint32_t nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSEventModifierFlags nsCocoaUtils::ConvertWidgetModifiersToMacModifierFlags(
|
||||
nsIWidget::Modifiers aNativeModifiers) {
|
||||
if (!aNativeModifiers) {
|
||||
return 0;
|
||||
}
|
||||
struct ModifierFlagMapEntry {
|
||||
nsIWidget::Modifiers mWidgetModifier;
|
||||
NSEventModifierFlags mModifierFlags;
|
||||
};
|
||||
static constexpr ModifierFlagMapEntry sModifierFlagMap[] = {
|
||||
{nsIWidget::CAPS_LOCK, NSEventModifierFlagCapsLock},
|
||||
{nsIWidget::SHIFT_L, NSEventModifierFlagShift | 0x0002},
|
||||
{nsIWidget::SHIFT_R, NSEventModifierFlagShift | 0x0004},
|
||||
{nsIWidget::CTRL_L, NSEventModifierFlagControl | 0x0001},
|
||||
{nsIWidget::CTRL_R, NSEventModifierFlagControl | 0x2000},
|
||||
{nsIWidget::ALT_L, NSEventModifierFlagOption | 0x0020},
|
||||
{nsIWidget::ALT_R, NSEventModifierFlagOption | 0x0040},
|
||||
{nsIWidget::COMMAND_L, NSEventModifierFlagCommand | 0x0008},
|
||||
{nsIWidget::COMMAND_R, NSEventModifierFlagCommand | 0x0010},
|
||||
{nsIWidget::NUMERIC_KEY_PAD, NSEventModifierFlagNumericPad},
|
||||
{nsIWidget::HELP, NSEventModifierFlagHelp},
|
||||
{nsIWidget::FUNCTION, NSEventModifierFlagFunction}};
|
||||
|
||||
NSEventModifierFlags modifierFlags = 0;
|
||||
for (const ModifierFlagMapEntry& entry : sModifierFlagMap) {
|
||||
if (aNativeModifiers & entry.mWidgetModifier) {
|
||||
modifierFlags |= entry.mModifierFlags;
|
||||
}
|
||||
}
|
||||
return modifierFlags;
|
||||
}
|
||||
|
||||
NSMutableAttributedString* nsCocoaUtils::GetNSMutableAttributedString(
|
||||
const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRanges, const bool aIsVertical,
|
||||
const CGFloat aBackingScaleFactor) {
|
||||
|
@ -305,7 +305,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||
virtual void SetDrawsInTitlebar(bool aState) override;
|
||||
virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIWidget::Modifiers aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage, double aDeltaX,
|
||||
|
@ -2443,15 +2443,15 @@ void nsCocoaWindow::SetDrawsInTitlebar(bool aState) {
|
||||
|
||||
NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIWidget::Modifiers aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
|
||||
|
||||
AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
if (mPopupContentView)
|
||||
if (mPopupContentView) {
|
||||
return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, aModifierFlags,
|
||||
nullptr);
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
|
||||
|
@ -1026,6 +1026,41 @@ uint32_t KeymapWrapper::ComputeKeyModifiers(guint aModifierState) {
|
||||
return keyModifiers;
|
||||
}
|
||||
|
||||
/* static */
|
||||
guint KeymapWrapper::ConvertWidgetModifierToGdkState(
|
||||
nsIWidget::Modifiers aNativeModifiers) {
|
||||
if (!aNativeModifiers) {
|
||||
return 0;
|
||||
}
|
||||
struct ModifierMapEntry {
|
||||
nsIWidget::Modifiers mWidgetModifier;
|
||||
Modifier mModifier;
|
||||
};
|
||||
// TODO: Currently, we don't treat L/R of each modifier on Linux.
|
||||
// TODO: No proper native modifier for Level5.
|
||||
static constexpr ModifierMapEntry sModifierMap[] = {
|
||||
{nsIWidget::CAPS_LOCK, Modifier::CAPS_LOCK},
|
||||
{nsIWidget::NUM_LOCK, Modifier::NUM_LOCK},
|
||||
{nsIWidget::SHIFT_L, Modifier::SHIFT},
|
||||
{nsIWidget::SHIFT_R, Modifier::SHIFT},
|
||||
{nsIWidget::CTRL_L, Modifier::CTRL},
|
||||
{nsIWidget::CTRL_R, Modifier::CTRL},
|
||||
{nsIWidget::ALT_L, Modifier::ALT},
|
||||
{nsIWidget::ALT_R, Modifier::ALT},
|
||||
{nsIWidget::ALTGRAPH, Modifier::LEVEL3},
|
||||
{nsIWidget::COMMAND_L, Modifier::SUPER},
|
||||
{nsIWidget::COMMAND_R, Modifier::SUPER}};
|
||||
|
||||
guint state = 0;
|
||||
KeymapWrapper* instance = GetInstance();
|
||||
for (const ModifierMapEntry& entry : sModifierMap) {
|
||||
if (aNativeModifiers & entry.mWidgetModifier) {
|
||||
state |= instance->GetModifierMask(entry.mModifier);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void KeymapWrapper::InitInputEvent(WidgetInputEvent& aInputEvent,
|
||||
guint aModifierState) {
|
||||
|
@ -8,8 +8,9 @@
|
||||
#ifndef __nsGdkKeyUtils_h__
|
||||
#define __nsGdkKeyUtils_h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <X11/XKBlib.h>
|
||||
@ -108,6 +109,13 @@ class KeymapWrapper {
|
||||
*/
|
||||
static uint32_t ComputeKeyModifiers(guint aModifierState);
|
||||
|
||||
/**
|
||||
* Convert native modifiers for `nsIWidget::SynthesizeNative*()` to
|
||||
* GDK's state.
|
||||
*/
|
||||
static guint ConvertWidgetModifierToGdkState(
|
||||
nsIWidget::Modifiers aNativeModifiers);
|
||||
|
||||
/**
|
||||
* InitInputEvent() initializes the aInputEvent with aModifierState.
|
||||
*/
|
||||
|
@ -7868,10 +7868,9 @@ LayoutDeviceIntRect nsWindow::GdkRectToDevicePixels(GdkRectangle rect) {
|
||||
rect.height * scale);
|
||||
}
|
||||
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) {
|
||||
AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
|
||||
if (!mGdkWindow) {
|
||||
@ -7891,6 +7890,8 @@ nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
memset(&event, 0, sizeof(GdkEvent));
|
||||
event.type = (GdkEventType)aNativeMessage;
|
||||
event.button.button = 1;
|
||||
event.button.state =
|
||||
KeymapWrapper::ConvertWidgetModifierToGdkState(aModifierFlags);
|
||||
event.button.window = mGdkWindow;
|
||||
event.button.time = GDK_CURRENT_TIME;
|
||||
|
||||
@ -7910,6 +7911,7 @@ nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
// We don't support specific events other than button-press/release. In all
|
||||
// other cases we'll synthesize a motion event that will be emitted by
|
||||
// gdk_display_warp_pointer().
|
||||
// XXX How to activate native modifier for the other events?
|
||||
GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
|
||||
GdkPoint point = DevicePixelsToGdkPointRoundDown(aPoint);
|
||||
gdk_display_warp_pointer(display, screen, point.x, point.y);
|
||||
|
@ -341,14 +341,15 @@ class nsWindow final : public nsBaseWidget {
|
||||
|
||||
virtual void ReparentNativeWidget(nsIWidget* aNewParent) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override {
|
||||
return SynthesizeNativeMouseEvent(aPoint, GDK_MOTION_NOTIFY, 0, aObserver);
|
||||
return SynthesizeNativeMouseEvent(aPoint, GDK_MOTION_NOTIFY,
|
||||
nsIWidget::Modifiers::NO_MODIFIERS,
|
||||
aObserver);
|
||||
}
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseScrollEvent(
|
||||
|
@ -432,10 +432,9 @@ nsresult HeadlessWidget::DispatchEvent(WidgetGUIEvent* aEvent,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult HeadlessWidget::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
nsresult HeadlessWidget::SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) {
|
||||
AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
EventMessage msg;
|
||||
switch (aNativeMessage) {
|
||||
|
@ -131,13 +131,13 @@ class HeadlessWidget : public nsBaseWidget {
|
||||
virtual nsresult DispatchEvent(WidgetGUIEvent* aEvent,
|
||||
nsEventStatus& aStatus) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override {
|
||||
return SynthesizeNativeMouseEvent(aPoint, MOZ_HEADLESS_MOUSE_MOVE, 0,
|
||||
return SynthesizeNativeMouseEvent(aPoint, MOZ_HEADLESS_MOUSE_MOVE,
|
||||
nsIWidget::Modifiers::NO_MODIFIERS,
|
||||
aObserver);
|
||||
};
|
||||
|
||||
|
@ -487,10 +487,9 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) override {
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override {
|
||||
mozilla::widget::AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
@ -1515,7 +1515,10 @@ class nsIWidget : public nsISupports {
|
||||
mozilla::WidgetGUIEvent* aEvent, int32_t aHorizontal,
|
||||
int32_t aVertical) = 0;
|
||||
|
||||
enum Modifiers {
|
||||
// TODO: Make this an enum class with MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS or
|
||||
// EnumSet class.
|
||||
enum Modifiers : uint32_t {
|
||||
NO_MODIFIERS = 0x00000000,
|
||||
CAPS_LOCK = 0x00000001, // when CapsLock is active
|
||||
NUM_LOCK = 0x00000002, // when NumLock is active
|
||||
SHIFT_L = 0x00000100,
|
||||
@ -1576,15 +1579,15 @@ class nsIWidget : public nsISupports {
|
||||
* @param aNativeMessage *platform-specific* event type (e.g. on Mac,
|
||||
* NSEventTypeMouseMoved; on Windows, MOUSEEVENTF_MOVE, MOUSEEVENTF_LEFTDOWN
|
||||
* etc)
|
||||
* @param aModifierFlags *platform-specific* modifier flags (ignored
|
||||
* on Windows)
|
||||
* @param aModifierFlags Some values of nsIWidget::Modifiers.
|
||||
* FYI: On Windows, Android and Headless widget on all
|
||||
* platroms, this hasn't been handled yet.
|
||||
* @param aObserver the observer that will get notified once the events
|
||||
* have been dispatched.
|
||||
*/
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) = 0;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) = 0;
|
||||
|
||||
/**
|
||||
* A shortcut to SynthesizeNativeMouseEvent, abstracting away the native
|
||||
|
@ -17,23 +17,6 @@
|
||||
<div id="target"></div>
|
||||
<script type="application/javascript">
|
||||
|
||||
function synthesizeNativeMouseClickWithControl(aTarget, aOffsetX, aOffsetY) {
|
||||
const NSEventTypeLeftMouseDown = 1;
|
||||
const NSEventTypeLeftMouseUp = 2;
|
||||
|
||||
const NSEventModifierFlagControl = 1 << 18;
|
||||
|
||||
let utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
|
||||
let rect = aTarget.getBoundingClientRect();
|
||||
let x = aOffsetX + window.mozInnerScreenX + rect.left;
|
||||
let y = aOffsetY + window.mozInnerScreenY + rect.top;
|
||||
let scale = utils.screenPixelsPerCSSPixel;
|
||||
|
||||
utils.sendNativeMouseEvent(x * scale, y * scale, NSEventTypeLeftMouseDown, NSEventModifierFlagControl, aTarget);
|
||||
utils.sendNativeMouseEvent(x * scale, y * scale, NSEventTypeLeftMouseUp, NSEventModifierFlagControl, aTarget);
|
||||
}
|
||||
|
||||
function waitAndCheckMouseEvents(aTarget, aExpectedEvents) {
|
||||
return new Promise((aResolve, aReject) => {
|
||||
let timer;
|
||||
@ -92,7 +75,12 @@ add_task(async function TestMouseClickWithControl() {
|
||||
["contextmenu", 0, true],
|
||||
["mouseup", 0, true],
|
||||
["click", 0, true]]);
|
||||
synthesizeNativeMouseClickWithControl(target, 10, 10);
|
||||
synthesizeNativeMouseClick({
|
||||
target,
|
||||
offsetX: 10,
|
||||
offsetY: 10,
|
||||
modifiers: { ctrlKey: true },
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
@ -106,7 +94,12 @@ add_task(async function TestOldBehavior() {
|
||||
["contextmenu", 2, true],
|
||||
["mouseup", 2, true],
|
||||
["auxclick", 2, true]]);
|
||||
synthesizeNativeMouseClickWithControl(target, 10, 10);
|
||||
synthesizeNativeMouseClick({
|
||||
target,
|
||||
offsetX: 10,
|
||||
offsetY: 10,
|
||||
modifiers: { ctrlKey: true },
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
</script>
|
||||
|
@ -6604,10 +6604,9 @@ nsresult nsWindow::SynthesizeNativeKeyEvent(
|
||||
aUnmodifiedCharacters);
|
||||
}
|
||||
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) {
|
||||
nsresult nsWindow::SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) {
|
||||
AutoObserverNotifier notifier(aObserver, "mouseevent");
|
||||
|
||||
if (aNativeMessage == MOUSEEVENTF_MOVE) {
|
||||
@ -6621,6 +6620,13 @@ nsresult nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
INPUT input;
|
||||
memset(&input, 0, sizeof(input));
|
||||
|
||||
// TODO (bug 1693240):
|
||||
// Now, we synthesize native mouse events asynchronously since we want to
|
||||
// synthesize the event on the front window at the point. However, Windows
|
||||
// does not provide a way to set modifier only while a mouse message is
|
||||
// being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
|
||||
// need a trick for handling it.
|
||||
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwFlags = aNativeMessage;
|
||||
::SendInput(1, &input, sizeof(INPUT));
|
||||
|
@ -222,14 +222,15 @@ class nsWindow final : public nsWindowBase {
|
||||
int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
|
||||
uint32_t aModifierFlags, const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
|
||||
uint32_t aNativeMessage,
|
||||
uint32_t aModifierFlags,
|
||||
nsIObserver* aObserver) override;
|
||||
virtual nsresult SynthesizeNativeMouseEvent(
|
||||
LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
|
||||
nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override;
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
|
||||
nsIObserver* aObserver) override {
|
||||
return SynthesizeNativeMouseEvent(aPoint, MOUSEEVENTF_MOVE, 0, aObserver);
|
||||
return SynthesizeNativeMouseEvent(aPoint, MOUSEEVENTF_MOVE,
|
||||
nsIWidget::Modifiers::NO_MODIFIERS,
|
||||
aObserver);
|
||||
}
|
||||
|
||||
virtual nsresult SynthesizeNativeMouseScrollEvent(
|
||||
|
Loading…
Reference in New Issue
Block a user