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(