From b63882dde8b8b5b7132ebd7e385eb4fdb781f928 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 24 Feb 2021 01:27:07 +0000 Subject: [PATCH] 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 --- accessible/mac/mozAccessible.mm | 4 +- dom/base/nsDOMWindowUtils.cpp | 100 ++++++++++++- dom/interfaces/base/nsIDOMWindowUtils.idl | 28 +++- dom/ipc/BrowserParent.cpp | 6 +- .../mochitest/apz_test_native_event_utils.js | 71 ++++++++- .../Harness_sanity/test_sanityEventUtils.html | 141 ++++++++++++++++++ .../mochitest/tests/SimpleTest/EventUtils.js | 51 ++++--- widget/PuppetWidget.cpp | 5 +- widget/PuppetWidget.h | 7 +- widget/android/nsWindow.cpp | 8 +- widget/android/nsWindow.h | 2 +- widget/cocoa/TextInputHandler.mm | 23 +-- widget/cocoa/nsChildView.h | 5 +- widget/cocoa/nsChildView.mm | 7 +- widget/cocoa/nsCocoaUtils.h | 8 + widget/cocoa/nsCocoaUtils.mm | 32 ++++ widget/cocoa/nsCocoaWindow.h | 2 +- widget/cocoa/nsCocoaWindow.mm | 6 +- widget/gtk/nsGtkKeyUtils.cpp | 35 +++++ widget/gtk/nsGtkKeyUtils.h | 10 +- widget/gtk/nsWindow.cpp | 10 +- widget/gtk/nsWindow.h | 11 +- widget/headless/HeadlessWidget.cpp | 7 +- widget/headless/HeadlessWidget.h | 10 +- widget/nsBaseWidget.h | 7 +- widget/nsIWidget.h | 17 ++- .../test_mouse_event_with_control_on_mac.html | 31 ++-- widget/windows/nsWindow.cpp | 14 +- widget/windows/nsWindow.h | 11 +- 29 files changed, 539 insertions(+), 130 deletions(-) diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm index 951778a09a6b..fbf0d114ebd5 100644 --- a/accessible/mac/mozAccessible.mm +++ b/accessible/mac/mozAccessible.mm @@ -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 { diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index cdb923ebc316..97d439659399 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -920,9 +920,96 @@ nsresult nsDOMWindowUtils::SendTouchEventCommon( return rv; } +static_assert( + static_cast(nsIWidget::Modifiers::CAPS_LOCK) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_CAPS_LOCK), + "Need to sync CapsLock value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::NUM_LOCK) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_NUM_LOCK), + "Need to sync NumLock value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::SHIFT_L) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_SHIFT_LEFT), + "Need to sync ShiftLeft value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::SHIFT_R) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_SHIFT_RIGHT), + "Need to sync ShiftRight value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::CTRL_L) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_CONTROL_LEFT), + "Need to sync ControlLeft value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::CTRL_R) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_CONTROL_RIGHT), + "Need to sync ControlRight value between nsIWidget::Modifiers " + "and nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::ALT_L) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_LEFT), + "Need to sync AltLeft value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::ALT_R) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_RIGHT), + "Need to sync AltRight value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::COMMAND_L) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_COMMAND_LEFT), + "Need to sync CommandLeft value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::COMMAND_R) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_COMMAND_RIGHT), + "Need to sync CommandRight value between nsIWidget::Modifiers " + "and nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::HELP) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_HELP), + "Need to sync Help value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::ALTGRAPH) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_ALT_GRAPH), + "Need to sync AltGraph value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert( + static_cast(nsIWidget::Modifiers::FUNCTION) == + static_cast(nsIDOMWindowUtils::NATIVE_MODIFIER_FUNCTION), + "Need to sync Function value between nsIWidget::Modifiers and " + "nsIDOMWindowUtils"); +static_assert(static_cast(nsIWidget::Modifiers::NUMERIC_KEY_PAD) == + static_cast( + 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( + 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(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(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( + NewRunnableMethod( "nsIWidget::SynthesizeNativeMouseEvent", widget, &nsIWidget::SynthesizeNativeMouseEvent, LayoutDeviceIntPoint(aScreenX, aScreenY), aNativeMessage, - aModifierFlags, aObserver))); + GetWidgetModifiers(aModifierFlags), aObserver))); return NS_OK; } diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index f115889acabb..930bccdd4717 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -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); /** diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index b87526865b35..81a9033779a7 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -1784,8 +1784,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseEvent( AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent"); nsCOMPtr widget = GetWidget(); if (widget) { - widget->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, aModifierFlags, - responder.GetObserver()); + widget->SynthesizeNativeMouseEvent( + aPoint, aNativeMessage, + static_cast(aModifierFlags), + responder.GetObserver()); } return IPC_OK(); } diff --git a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js index aa82e90c75c5..086708c36f5d 100644 --- a/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js +++ b/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js @@ -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 ); diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html b/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html index 6135c65adf11..e428ef158c10 100644 --- a/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html +++ b/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html @@ -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(); diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index f6683fba9173..a52decc52e3a 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -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; } diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 3329fb6b8732..a57ab1746265 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -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(aModifierFlags), + notifier.SaveObserver()); return NS_OK; } diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index 3b8e2dfd27a8..828c58af9201 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -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( diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 118b9b0dcc14..3244f9850ec4 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -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), diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 76b3a7b32d6b..183bca8546f9 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -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; diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index 52c318a2436d..92a1eae1fbc1 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -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(aModifierFlags)); NSInteger windowNumber = [[mView window] windowNumber]; bool sendFlagsChangedEvent = IsModifierKey(aNativeKeyCode); NSEventType eventType = sendFlagsChangedEvent ? NSEventTypeFlagsChanged : NSEventTypeKeyDown; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 6303ba06c110..2ba4ebe3404f 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -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, diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 2b2572cd66d4..cf94282cfaef 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -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 diff --git a/widget/cocoa/nsCocoaUtils.h b/widget/cocoa/nsCocoaUtils.h index 639a9e0d1782..5f815e644796 100644 --- a/widget/cocoa/nsCocoaUtils.h +++ b/widget/cocoa/nsCocoaUtils.h @@ -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 */ diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index 9668a86ba71b..b111e6873fe0 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -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& aFontRanges, const bool aIsVertical, const CGFloat aBackingScaleFactor) { diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 1c8c2cb82fa8..f0faa21a8b92 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -305,7 +305,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { virtual void SetDrawsInTitlebar(bool aState) override; virtual void UpdateThemeGeometries(const nsTArray& 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, diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 8ca3253f6007..30d941c6440c 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -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); diff --git a/widget/gtk/nsGtkKeyUtils.cpp b/widget/gtk/nsGtkKeyUtils.cpp index 41a679eab721..67d1cea404b7 100644 --- a/widget/gtk/nsGtkKeyUtils.cpp +++ b/widget/gtk/nsGtkKeyUtils.cpp @@ -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) { diff --git a/widget/gtk/nsGtkKeyUtils.h b/widget/gtk/nsGtkKeyUtils.h index 3354cf2feeb9..07d60c27a60a 100644 --- a/widget/gtk/nsGtkKeyUtils.h +++ b/widget/gtk/nsGtkKeyUtils.h @@ -8,8 +8,9 @@ #ifndef __nsGdkKeyUtils_h__ #define __nsGdkKeyUtils_h__ -#include "nsTArray.h" #include "mozilla/EventForwards.h" +#include "nsIWidget.h" +#include "nsTArray.h" #include #include @@ -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. */ diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index d3fce2b16a31..b063c46a3e90 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -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); diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index ef53bb508d0f..c61c68300145 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -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( diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp index 46e24a8c457f..20ae275e1b83 100644 --- a/widget/headless/HeadlessWidget.cpp +++ b/widget/headless/HeadlessWidget.cpp @@ -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) { diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h index 7dbbdca1c349..8ae9a0059575 100644 --- a/widget/headless/HeadlessWidget.h +++ b/widget/headless/HeadlessWidget.h @@ -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); }; diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index a1d885e582ed..c06af15d5795 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -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; } diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index a3b5756b2bda..388322c31649 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -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 diff --git a/widget/tests/test_mouse_event_with_control_on_mac.html b/widget/tests/test_mouse_event_with_control_on_mac.html index 85e206ea9572..28f93ec3bd01 100644 --- a/widget/tests/test_mouse_event_with_control_on_mac.html +++ b/widget/tests/test_mouse_event_with_control_on_mac.html @@ -17,23 +17,6 @@
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 030d358aec65..07577526196a 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -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)); diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index c01aab1dae61..97b748e05707 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -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(