Repository: electron/electron
Pull request: fix: context-menu event emitted in draggable regions

#### Description of Change

Closes https://github.com/electron/electron/issues/45301.

Takes a similar approach as https://github.com/electron/electron/pull/44978 - we intercept potential right clicks and disable draggable regions temporarily to allow the events to punch through. Draggable regions only respond to left-click dragging, but the system will still suppress right-clicks in a draggable region, so this effectively allows `contextmenu` events to be dispatched as expected. I'm also looking into addressing this on Windows, but we'll probably need to take a different approach.

Tested with https://gist.github.com/bpasero/b04271deaacf0e099b0a0fa2289e8d28

cc @bpasero @deepak1556

![drag-emit](https://github.com/user-attachments/assets/b098f407-33f0-4af9-be8d-14962213866e)

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Fixed an issue where `contextmenu` events wouldn't be correctly dispatched in draggable regions on Linux.


Changed files:
filename: shell/browser/ui/electron_desktop_window_tree_host_linux.cc
patch: @@ -12,6 +12,7 @@
 
 #include "base/feature_list.h"
 #include "base/i18n/rtl.h"
+#include "shell/browser/api/electron_api_web_contents.h"
 #include "shell/browser/native_window_features.h"
 #include "shell/browser/native_window_views.h"
 #include "shell/browser/ui/views/client_frame_view_linux.h"
@@ -241,4 +242,23 @@ void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
     SizeConstraintsChanged();
   }
 }
+
+void ElectronDesktopWindowTreeHostLinux::DispatchEvent(ui::Event* event) {
+  if (event->IsMouseEvent()) {
+    auto* mouse_event = static_cast<ui::MouseEvent*>(event);
+    bool is_mousedown = mouse_event->type() == ui::EventType::kMousePressed;
+    bool is_system_menu_trigger =
+        is_mousedown &&
+        (mouse_event->IsRightMouseButton() ||
+         (mouse_event->IsLeftMouseButton() && mouse_event->IsControlDown()));
+    if (is_system_menu_trigger) {
+      electron::api::WebContents::SetDisableDraggableRegions(true);
+      views::DesktopWindowTreeHostLinux::DispatchEvent(event);
+      electron::api::WebContents::SetDisableDraggableRegions(false);
+      return;
+    }
+  }
+  views::DesktopWindowTreeHostLinux::DispatchEvent(event);
+}
+
 }  // namespace electron

filename: shell/browser/ui/electron_desktop_window_tree_host_linux.h
patch: @@ -60,6 +60,7 @@ class ElectronDesktopWindowTreeHostLinux
 
   // views::DesktopWindowTreeHostLinux:
   void UpdateFrameHints() override;
+  void DispatchEvent(ui::Event* event) override;
 
  private:
   void UpdateWindowState(ui::PlatformWindowState new_state);

filename: spec/api-web-contents-spec.ts
patch: @@ -2861,18 +2861,18 @@ describe('webContents module', () => {
 
       await once(w.webContents, 'context-menu');
       await setTimeout(100);
-
       expect(contextMenuEmitCount).to.equal(1);
     });
 
-    it('emits when right-clicked in page in a draggable region', async () => {
+    ifit(process.platform !== 'win32')('emits when right-clicked in page in a draggable region', async () => {
       const w = new BrowserWindow({ show: false });
       await w.loadFile(path.join(fixturesPath, 'pages', 'draggable-page.html'));
 
       const promise = once(w.webContents, 'context-menu') as Promise<[any, Electron.ContextMenuParams]>;
 
       // Simulate right-click to create context-menu event.
-      const opts = { x: 0, y: 0, button: 'right' as const };
+      const midPoint = w.getBounds().width / 2;
+      const opts = { x: midPoint, y: midPoint, button: 'right' as const };
       w.webContents.sendInputEvent({ ...opts, type: 'mouseDown' });
       w.webContents.sendInputEvent({ ...opts, type: 'mouseUp' });