Skip to content

Commit a816c77

Browse files
committed
Bugfix: Drag&drop didn't work right
Fix drag swizzle after wry upgrade - wry 0.54+ with objc2 0.6 no longer registers a stable `WryWebView` ObjC class name, so the hardcoded `AnyClass::get(c"WryWebView")` lookup failed silently - Now discovers the webview class from the live instance via `with_webview` + `msg_send![obj, class]` — resilient to future wry renames
1 parent 1479108 commit a816c77

2 files changed

Lines changed: 90 additions & 52 deletions

File tree

apps/desktop/src-tauri/src/drag_image_detection.rs

Lines changed: 88 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Native drag interception for macOS via method swizzling on WryWebView.
1+
//! Native drag interception for macOS via method swizzling on wry's webview class.
22
//!
33
//! Swizzles `draggingEntered:`, `draggingUpdated:`, and `draggingExited:` to:
44
//! 1. Read drag image dimensions via `enumerateDraggingItems` (for overlay suppression)
@@ -11,8 +11,9 @@
1111
//!
1212
//! ## Resilience
1313
//!
14-
//! All native API calls are guarded against class/method removal. If wry renames its
15-
//! internal webview class or macOS deprecates APIs we rely on, the swizzle degrades gracefully:
14+
//! All native API calls are guarded against method removal. The webview class is discovered
15+
//! from the actual webview instance (not a hardcoded name), so wry version changes are safe.
16+
//! If macOS deprecates APIs we rely on, the swizzle degrades gracefully:
1617
//! - Drag image detection disabled → the DOM overlay is always shown (redundant but functional)
1718
//! - Modifier key detection disabled → falls back to JS keydown/keyup (works when webview has focus)
1819
//! - Image swapping disabled → self-drags show the OS drag image over the window (functional)
@@ -30,7 +31,7 @@ use objc2::{msg_send, sel};
3031
use objc2_app_kit::{NSDragOperation, NSDraggingItem, NSDraggingItemEnumerationOptions};
3132
use objc2_foundation::{NSDictionary, NSInteger, NSRect, NSSize};
3233
use serde::Serialize;
33-
use tauri::{AppHandle, Emitter};
34+
use tauri::{AppHandle, Emitter, Manager};
3435

3536
use crate::drag_image_swap;
3637

@@ -72,69 +73,107 @@ pub(crate) fn warn_once(flag: &AtomicBool, msg: &str) {
7273
}
7374
}
7475

75-
/// Installs swizzles on WryWebView. Call once during app setup.
76+
/// Installs swizzles on wry's webview class. Call once during app setup after the
77+
/// webview is created (e.g. `RunEvent::Ready`).
78+
///
79+
/// Gets the ObjC class from the actual webview instance rather than looking up a
80+
/// hardcoded class name, so this is resilient to wry renaming its internal class.
7681
pub fn install(app_handle: AppHandle) {
77-
APP_HANDLE.set(app_handle).ok();
82+
APP_HANDLE.set(app_handle.clone()).ok();
7883

79-
unsafe {
80-
let Some(cls) = AnyClass::get(c"WryWebView") else {
81-
log::warn!(
82-
"drag_image_detection: WryWebView class not found — swizzle skipped. \
83-
Drag image detection and modifier tracking during drags are disabled. \
84-
This is likely caused by a wry update that renamed the webview class; \
85-
search wry's source for the ObjC class name and update the c\"WryWebView\" \
86-
lookup in drag_image_detection.rs."
87-
);
88-
return;
89-
};
84+
let Some((_label, webview_window)) = app_handle.webview_windows().into_iter().next() else {
85+
log::warn!(
86+
"drag_image_detection: no webview windows found — swizzle skipped. \
87+
Drag image detection and modifier tracking during drags are disabled."
88+
);
89+
return;
90+
};
91+
92+
if let Err(e) = webview_window.with_webview(|webview| {
93+
unsafe { install_swizzles(webview.inner()) };
94+
}) {
95+
log::warn!(
96+
"drag_image_detection: with_webview failed ({e}) — swizzle skipped. \
97+
Drag image detection and modifier tracking during drags are disabled."
98+
);
99+
}
100+
}
90101

91-
// Swizzle draggingEntered:
92-
if let Some(method) = cls.instance_method(sel!(draggingEntered:)) {
93-
ORIGINAL_ENTERED_IMP.set(method.implementation()).ok();
102+
/// Performs the actual swizzle installation given the native webview pointer.
103+
///
104+
/// # Safety
105+
/// `webview_ptr` must be a valid pointer to the ObjC webview object (from `PlatformWebview::inner()`).
106+
unsafe fn install_swizzles(webview_ptr: *mut std::ffi::c_void) {
107+
let obj = webview_ptr as *const AnyObject;
108+
if obj.is_null() {
109+
log::warn!(
110+
"drag_image_detection: native webview pointer is null — swizzle skipped. \
111+
Drag image detection and modifier tracking during drags are disabled."
112+
);
113+
return;
114+
}
115+
116+
let cls: *const AnyClass = unsafe { msg_send![obj, class] };
117+
let Some(cls) = (unsafe { cls.as_ref() }) else {
118+
log::warn!(
119+
"drag_image_detection: could not get ObjC class from webview — swizzle skipped. \
120+
Drag image detection and modifier tracking during drags are disabled."
121+
);
122+
return;
123+
};
124+
125+
// Swizzle draggingEntered:
126+
if let Some(method) = cls.instance_method(sel!(draggingEntered:)) {
127+
ORIGINAL_ENTERED_IMP.set(method.implementation()).ok();
128+
unsafe {
94129
method.set_implementation(std::mem::transmute::<*const (), Imp>(
95130
swizzled_dragging_entered as *const (),
96131
));
97-
} else {
98-
log::warn!(
99-
"drag_image_detection: draggingEntered: not found on WryWebView — \
100-
drag image size detection is disabled. \
101-
Wry may have changed how it implements NSDraggingDestination; \
102-
check wry's drag-and-drop event handling in its ObjC layer."
103-
);
104132
}
133+
} else {
134+
log::warn!(
135+
"drag_image_detection: draggingEntered: not found on webview class — \
136+
drag image size detection is disabled. \
137+
Wry may have changed how it implements NSDraggingDestination; \
138+
check wry's drag-and-drop event handling in its ObjC layer."
139+
);
140+
}
105141

106-
// Swizzle draggingUpdated:
107-
if let Some(method) = cls.instance_method(sel!(draggingUpdated:)) {
108-
ORIGINAL_UPDATED_IMP.set(method.implementation()).ok();
142+
// Swizzle draggingUpdated:
143+
if let Some(method) = cls.instance_method(sel!(draggingUpdated:)) {
144+
ORIGINAL_UPDATED_IMP.set(method.implementation()).ok();
145+
unsafe {
109146
method.set_implementation(std::mem::transmute::<*const (), Imp>(
110147
swizzled_dragging_updated as *const (),
111148
));
112-
} else {
113-
log::warn!(
114-
"drag_image_detection: draggingUpdated: not found on WryWebView — \
115-
live modifier key tracking during drags is disabled. \
116-
Wry may have changed how it implements NSDraggingDestination; \
117-
check wry's drag-and-drop event handling in its ObjC layer."
118-
);
119149
}
150+
} else {
151+
log::warn!(
152+
"drag_image_detection: draggingUpdated: not found on webview class — \
153+
live modifier key tracking during drags is disabled. \
154+
Wry may have changed how it implements NSDraggingDestination; \
155+
check wry's drag-and-drop event handling in its ObjC layer."
156+
);
157+
}
120158

121-
// Swizzle draggingExited: for self-drag image swapping (transparent → rich on window exit)
122-
if let Some(method) = cls.instance_method(sel!(draggingExited:)) {
123-
ORIGINAL_EXITED_IMP.set(method.implementation()).ok();
159+
// Swizzle draggingExited: for self-drag image swapping (transparent → rich on window exit)
160+
if let Some(method) = cls.instance_method(sel!(draggingExited:)) {
161+
ORIGINAL_EXITED_IMP.set(method.implementation()).ok();
162+
unsafe {
124163
method.set_implementation(std::mem::transmute::<*const (), Imp>(
125164
swizzled_dragging_exited as *const (),
126165
));
127-
} else {
128-
log::warn!(
129-
"drag_image_detection: draggingExited: not found on WryWebView — \
130-
drag image swapping on window exit is disabled. \
131-
Wry may have changed how it implements NSDraggingDestination; \
132-
check wry's drag-and-drop event handling in its ObjC layer."
133-
);
134166
}
135-
136-
log::debug!("drag_image_detection: swizzles installed on WryWebView");
167+
} else {
168+
log::warn!(
169+
"drag_image_detection: draggingExited: not found on webview class — \
170+
drag image swapping on window exit is disabled. \
171+
Wry may have changed how it implements NSDraggingDestination; \
172+
check wry's drag-and-drop event handling in its ObjC layer."
173+
);
137174
}
175+
176+
log::debug!("drag_image_detection: swizzles installed on {:?}", cls.name());
138177
}
139178

140179
// --- Modifier key detection ---

apps/desktop/src-tauri/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -971,9 +971,8 @@ pub fn run() {
971971
.run(|_app, event| {
972972
match event {
973973
tauri::RunEvent::Ready => {
974-
// Install drag image detection swizzle now that the webview exists.
975-
// wry 0.54+ registers WryWebView lazily, so it's only available after
976-
// the first webview is created (which happens between setup() and Ready).
974+
// Install drag image detection swizzle. Needs a live webview to
975+
// discover wry's ObjC class, so it runs at Ready (not setup).
977976
#[cfg(target_os = "macos")]
978977
drag_image_detection::install(_app.clone());
979978
}

0 commit comments

Comments
 (0)