Skip to content

Commit 7ddb49b

Browse files
committed
[v2] fix: Files DragAndDrop bugs with OnDomReady and DisableWebViewDrop
This commit fixes the issues reported in #3563: 1. OnDomReady triggering multiple times during drag-and-drop: - Root cause: When files are dropped without proper event handling, the browser navigates to the dropped file, triggering navigation completion callbacks and thus OnDomReady again. - Fix: Register drag/drop event handlers early in the runtime JavaScript (main.js) before the user has a chance to drop files. These handlers call e.preventDefault() for file drops, preventing browser navigation. - Added setup() function in draganddrop.js that's called from main.js. - Additionally added OnDomReady single-fire guards on all platforms as a safety measure. 2. DisableWebViewDrop + EnableFileDrop conflict on Windows: - When both options were enabled, AllowExternalDrag(false) was called, which blocked ALL drag events including the ones needed for file drops. - Now only calls AllowExternalDrag(false) when DisableWebViewDrop is true AND EnableFileDrop is false. Platform-specific changes: - Windows: Fixed navigationCompleted to only call OnDomReady once - Linux: Added domReadySent flag to prevent multiple DomReady messages - macOS: Added domReadySent property to track if DomReady was sent Fixes #3563
1 parent 7b8355a commit 7ddb49b

9 files changed

Lines changed: 76 additions & 20 deletions

File tree

v2/internal/frontend/desktop/darwin/WailsContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151
@property bool devtoolsEnabled;
5252
@property bool defaultContextMenuEnabled;
53+
@property bool domReadySent;
5354

5455
@property (retain) WKUserContentController* userContentController;
5556

v2/internal/frontend/desktop/darwin/WailsContext.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,12 @@ - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURL
473473
}
474474

475475
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
476-
processMessage("DomReady");
476+
// Only send DomReady once to prevent OnDomReady callback from being called
477+
// multiple times (e.g., on navigation or page reload)
478+
if (!self.domReadySent) {
479+
self.domReadySent = YES;
480+
processMessage("DomReady");
481+
}
477482
}
478483

479484
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {

v2/internal/frontend/desktop/linux/window.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,11 +469,20 @@ gboolean UnFullscreen(gpointer data)
469469
return G_SOURCE_REMOVE;
470470
}
471471

472+
// Flag to track if DomReady has been sent - ensures OnDomReady callback is only called once
473+
static gboolean domReadySent = FALSE;
474+
472475
static void webviewLoadChanged(WebKitWebView *web_view, WebKitLoadEvent load_event, gpointer data)
473476
{
474477
if (load_event == WEBKIT_LOAD_FINISHED)
475478
{
476-
processMessage("DomReady");
479+
// Only send DomReady once to prevent OnDomReady callback from being called
480+
// multiple times (e.g., on navigation or page reload)
481+
if (!domReadySent)
482+
{
483+
domReadySent = TRUE;
484+
processMessage("DomReady");
485+
}
477486
}
478487
}
479488

v2/internal/frontend/desktop/windows/frontend.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,13 @@ func (f *Frontend) setupChromium() {
488488
chromium.AdditionalBrowserArgs = append(chromium.AdditionalBrowserArgs, arg)
489489
}
490490

491-
if f.frontendOptions.DragAndDrop != nil && f.frontendOptions.DragAndDrop.DisableWebViewDrop {
491+
// Only disable external drag if DisableWebViewDrop is true AND EnableFileDrop is false.
492+
// When EnableFileDrop is true, we need to allow external drag events so that the
493+
// JavaScript handlers can receive file drop events. The JavaScript will prevent
494+
// the default browser behavior (navigating to the dropped file).
495+
if f.frontendOptions.DragAndDrop != nil &&
496+
f.frontendOptions.DragAndDrop.DisableWebViewDrop &&
497+
!f.frontendOptions.DragAndDrop.EnableFileDrop {
492498
if err := chromium.AllowExternalDrag(false); err != nil {
493499
f.logger.Warning("WebView failed to set AllowExternalDrag to false!")
494500
}
@@ -901,10 +907,6 @@ func (f *Frontend) ExecJS(js string) {
901907
}
902908

903909
func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) {
904-
if f.frontendOptions.OnDomReady != nil {
905-
go f.frontendOptions.OnDomReady(f.ctx)
906-
}
907-
908910
if f.frontendOptions.Frameless && f.frontendOptions.DisableResize == false {
909911
f.ExecJS("window.wails.flags.enableResize = true;")
910912
}
@@ -918,6 +920,12 @@ func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.IC
918920
}
919921
f.hasStarted = true
920922

923+
// Only call OnDomReady on the first navigation completion to prevent
924+
// multiple calls when page reloads or navigates (e.g., during file drops)
925+
if f.frontendOptions.OnDomReady != nil {
926+
go f.frontendOptions.OnDomReady(f.ctx)
927+
}
928+
921929
// Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026
922930
err := f.chromium.Hide()
923931
if err != nil {

v2/internal/frontend/runtime/desktop/draganddrop.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {EventsOn, EventsOff} from "./events";
1414

1515
const flags = {
1616
registered: false,
17+
handlersSetup: false,
1718
defaultUseDropTarget: true,
1819
useDropTarget: true,
1920
nextDeactivate: null,
@@ -245,9 +246,9 @@ export function OnFileDrop(callback, useDropTarget) {
245246

246247
const uDTPT = typeof useDropTarget;
247248
flags.useDropTarget = uDTPT === "undefined" || uDTPT !== "boolean" ? flags.defaultUseDropTarget : useDropTarget;
248-
window.addEventListener('dragover', onDragOver);
249-
window.addEventListener('dragleave', onDragLeave);
250-
window.addEventListener('drop', onDrop);
249+
250+
// Ensure handlers are set up (idempotent - safe to call multiple times)
251+
setup();
251252

252253
let cb = callback;
253254
if (flags.useDropTarget) {
@@ -274,3 +275,19 @@ export function OnFileDropOff() {
274275
EventsOff("wails:file-drop");
275276
flags.registered = false;
276277
}
278+
279+
/**
280+
* setup installs the drag and drop handlers early to prevent browser navigation
281+
* when files are dropped. This is called from the runtime initialization to ensure
282+
* handlers are in place before the user has a chance to drop files.
283+
* The actual file processing only happens when enableWailsDragAndDrop flag is true.
284+
*/
285+
export function setup() {
286+
if (flags.handlersSetup) {
287+
return;
288+
}
289+
flags.handlersSetup = true;
290+
window.addEventListener('dragover', onDragOver);
291+
window.addEventListener('dragleave', onDragLeave);
292+
window.addEventListener('drop', onDrop);
293+
}

v2/internal/frontend/runtime/desktop/main.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,10 @@ window.addEventListener('contextmenu', function(e) {
216216
}
217217
});
218218

219+
// Setup drag and drop handlers early to prevent browser navigation when files are dropped.
220+
// This must be done before the user has a chance to drop files on the window.
221+
// The actual file processing only happens when enableWailsDragAndDrop flag is set to true
222+
// by the backend (via runtime:ready response).
223+
DragAndDrop.setup();
224+
219225
window.WailsInvoke("runtime:ready");

v2/internal/frontend/runtime/ipc_websocket.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

v2/internal/frontend/runtime/runtime_debug_desktop.js

Lines changed: 15 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

v2/internal/frontend/runtime/runtime_prod_desktop.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)