Repository: electron/electron (language: cpp)

Issue:
[Bug]: context menus cannot be displayed when "-webkit-app-region: drag" is enabled
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

23.1.0

### What operating system are you using?

macOS

### Operating System Version

macOS Ventura 13.2.1

### What arch are you using?

arm64 (including Apple Silicon)

### Last Known Working Electron version

22.x

### Expected Behavior

I want to show the context menu in "-webkit-app-region: drag" enabled area, 

### Actual Behavior

 No response and no reaction.

### Testcase Gist URL

https://gist.github.com/3fe6a69c1eba1d7f85caf7572ba77fd2

### Additional Information

_No response_


Discussion / hints:
Same on Windows 11 22H2.
https://gist.github.com/3591cfe18a50841115038de49c746606

Fix patch:
diff --git a/filenames.gni b/filenames.gni
index bb080c47222fd..b6c66b5a64386 100644
--- a/filenames.gni
+++ b/filenames.gni
@@ -153,7 +153,7 @@ filenames = {
     "shell/browser/notifications/mac/notification_presenter_mac.mm",
     "shell/browser/relauncher_mac.cc",
     "shell/browser/ui/certificate_trust_mac.mm",
-    "shell/browser/ui/cocoa/delayed_native_view_host.cc",
+    "shell/browser/ui/cocoa/delayed_native_view_host.mm",
     "shell/browser/ui/cocoa/delayed_native_view_host.h",
     "shell/browser/ui/cocoa/electron_bundle_mover.h",
     "shell/browser/ui/cocoa/electron_bundle_mover.mm",
diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h
index a14bd8b0962aa..763f22123aa8a 100644
--- a/shell/browser/api/electron_api_web_contents.h
+++ b/shell/browser/api/electron_api_web_contents.h
@@ -442,7 +442,13 @@ class WebContents : public ExclusiveAccessContext,
   // content::RenderWidgetHost::InputEventObserver:
   void OnInputEvent(const blink::WebInputEvent& event) override;
 
-  SkRegion* draggable_region() { return draggable_region_.get(); }
+  SkRegion* draggable_region() {
+    return force_non_draggable_ ? nullptr : draggable_region_.get();
+  }
+
+  void SetForceNonDraggable(bool force_non_draggable) {
+    force_non_draggable_ = force_non_draggable;
+  }
 
   // disable copy
   WebContents(const WebContents&) = delete;
@@ -826,6 +832,8 @@ class WebContents : public ExclusiveAccessContext,
 
   std::unique_ptr<SkRegion> draggable_region_;
 
+  bool force_non_draggable_ = false;
+
   base::WeakPtrFactory<WebContents> weak_factory_{this};
 };
 
diff --git a/shell/browser/ui/cocoa/delayed_native_view_host.cc b/shell/browser/ui/cocoa/delayed_native_view_host.cc
deleted file mode 100644
index 5efe09686c7c0..0000000000000
--- a/shell/browser/ui/cocoa/delayed_native_view_host.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2018 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
-
-namespace electron {
-
-DelayedNativeViewHost::DelayedNativeViewHost(gfx::NativeView native_view)
-    : native_view_(native_view) {}
-
-DelayedNativeViewHost::~DelayedNativeViewHost() = default;
-
-void DelayedNativeViewHost::ViewHierarchyChanged(
-    const views::ViewHierarchyChangedDetails& details) {
-  NativeViewHost::ViewHierarchyChanged(details);
-  if (details.is_add && GetWidget())
-    Attach(native_view_);
-}
-
-}  // namespace electron
diff --git a/shell/browser/ui/cocoa/delayed_native_view_host.h b/shell/browser/ui/cocoa/delayed_native_view_host.h
index e7020dba60715..eb02826c2b3e6 100644
--- a/shell/browser/ui/cocoa/delayed_native_view_host.h
+++ b/shell/browser/ui/cocoa/delayed_native_view_host.h
@@ -23,6 +23,7 @@ class DelayedNativeViewHost : public views::NativeViewHost {
   // views::View:
   void ViewHierarchyChanged(
       const views::ViewHierarchyChangedDetails& details) override;
+  bool OnMousePressed(const ui::MouseEvent& event) override;
 
  private:
   gfx::NativeView native_view_;
diff --git a/shell/browser/ui/cocoa/delayed_native_view_host.mm b/shell/browser/ui/cocoa/delayed_native_view_host.mm
new file mode 100644
index 0000000000000..98fc995e3071e
--- /dev/null
+++ b/shell/browser/ui/cocoa/delayed_native_view_host.mm
@@ -0,0 +1,37 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
+#include "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h"
+
+namespace electron {
+
+DelayedNativeViewHost::DelayedNativeViewHost(gfx::NativeView native_view)
+    : native_view_(native_view) {}
+
+DelayedNativeViewHost::~DelayedNativeViewHost() = default;
+
+void DelayedNativeViewHost::ViewHierarchyChanged(
+    const views::ViewHierarchyChangedDetails& details) {
+  NativeViewHost::ViewHierarchyChanged(details);
+  if (details.is_add && GetWidget())
+    Attach(native_view_);
+}
+
+bool DelayedNativeViewHost::OnMousePressed(const ui::MouseEvent& ui_event) {
+  // NativeViewHost::OnMousePressed normally isn't called, but
+  // NativeWidgetMacNSWindow specifically carves out an event here for
+  // right-mouse-button clicks. We want to forward them to the web content, so
+  // handle them here.
+  // See:
+  // https://source.chromium.org/chromium/chromium/src/+/main:components/remote_cocoa/app_shim/native_widget_mac_nswindow.mm;l=415-421;drc=a5af91924bafb85426e091c6035801990a6dc697
+  ElectronInspectableWebContentsView* inspectable_web_contents_view =
+      (ElectronInspectableWebContentsView*)native_view_.GetNativeNSView();
+  [inspectable_web_contents_view
+      redispatchContextMenuEvent:ui_event.native_event()];
+
+  return true;
+}
+
+}  // namespace electron
diff --git a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h
index a27cb9dd62e4d..60f7502358fb4 100644
--- a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h
+++ b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h
@@ -46,6 +46,8 @@ using electron::InspectableWebContentsViewMac;
     (const DevToolsContentsResizingStrategy&)strategy;
 - (void)setTitle:(NSString*)title;
 
+- (void)redispatchContextMenuEvent:(NSEvent*)theEvent;
+
 @end
 
 #endif  // ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_INSPECTABLE_WEB_CONTENTS_VIEW_H_
diff --git a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm
index bceea3a14728b..1790ed888737b 100644
--- a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm
+++ b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm
@@ -5,10 +5,12 @@
 #include "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h"
 
 #include "content/public/browser/render_widget_host_view.h"
+#include "shell/browser/api/electron_api_web_contents.h"
 #include "shell/browser/ui/cocoa/event_dispatching_window.h"
 #include "shell/browser/ui/inspectable_web_contents.h"
 #include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
 #include "shell/browser/ui/inspectable_web_contents_view_mac.h"
+#include "ui/base/cocoa/base_view.h"
 #include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
 
 @implementation ElectronInspectableWebContentsView
@@ -273,6 +275,27 @@ - (void)parentWindowBecameMain:(NSNotification*)notification {
     [self notifyDevToolsFocused];
 }
 
+- (void)redispatchContextMenuEvent:(NSEvent*)event {
+  DCHECK(event.type == NSEventTypeRightMouseDown ||
+         (event.type == NSEventTypeLeftMouseDown &&
+          (event.modifierFlags & NSEventModifierFlagControl)));
+  content::WebContents* contents =
+      inspectableWebContentsView_->inspectable_web_contents()->GetWebContents();
+  electron::api::WebContents* api_contents =
+      electron::api::WebContents::From(contents);
+  if (api_contents) {
+    // Temporarily pretend that the WebContents is fully non-draggable while we
+    // re-send the mouse event. This allows the re-dispatched event to "land"
+    // on the WebContents, instead of "falling through" back to the window.
+    api_contents->SetForceNonDraggable(true);
+    BaseView* contentsView = (BaseView*)contents->GetRenderWidgetHostView()
+                                 ->GetNativeView()
+                                 .GetNativeNSView();
+    [contentsView mouseEvent:event];
+    api_contents->SetForceNonDraggable(false);
+  }
+}
+
 #pragma mark - NSWindowDelegate
 
 - (void)windowWillClose:(NSNotification*)notification {


Test patch: