Skip to content

[bug] on_new_window never fires for target="_blank" anchor clicks (Windows/WebView2, WebviewUrl::External) #15872

Description

@contrerasdaniel142

Describe the bug

WebviewWindowBuilder::on_new_window never fires when a user clicks a real <a href="..." target="_blank"> anchor inside a window created with WebviewUrl::External(...), on Windows/WebView2.

This was found while building a desktop wrapper around web.whatsapp.com (loaded via WebviewUrl::External). Clicking any link inside a chat message does nothing at all — no new window, no navigation, no error.

Reproduction

let builder = WebviewWindowBuilder::new(app, &label, WebviewUrl::External(url))
    // ... other builder calls (user_agent, initialization_script, on_navigation, on_download) ...
    .on_new_window({
        let app = app.clone();
        move |url, _features| {
            // Neither of these ever run:
            unsafe {
                use windows::core::PCWSTR;
                use windows::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_OK};
                let msg: Vec<u16> = format!("on_new_window fired: {url}\0").encode_utf16().collect();
                let title: Vec<u16> = "diag\0".encode_utf16().collect();
                MessageBoxW(None, PCWSTR(msg.as_ptr()), PCWSTR(title.as_ptr()), MB_OK);
            }
            tauri::webview::NewWindowResponse::Deny
        }
    });

Then click any <a target="_blank"> link on the loaded external page.

Expected behavior

The on_new_window closure should be invoked, per the docs ("A new window is requested to be opened by the window.open API"), and MDN/Chromium semantics where target="_blank" anchor clicks also request a new browsing context.

Actual behavior

The closure never runs. Confirmed with escalating diagnostics, from least to most invasive:

  1. crate::dlog::log(...) (plain file write) inside the closure — never appears in the log file.
  2. A raw, synchronous MessageBoxW call inside the closure — no dialog ever appears, ruling out any async/threading/IPC confound.

Meanwhile, confirmed via a page-injected diagnostic (monkey-patched window.open + a capture-phase click listener, both using alert() so no console/IPC dependency) that:

  • The click target is a real anchor: <a href="https://google.com/" target="_blank"> (tested with multiple URLs, including plain https://google.com/, to rule out anything page/site-specific).
  • The page never calls window.open() — it relies purely on native browser handling of target="_blank", which is normal/expected behavior for any website.

So a genuine, native, user-initiated target="_blank" anchor click produces literally no event reaching on_new_window — not even a native win32 MessageBoxW invoked synchronously inside the handler appears.

Investigation notes

I reviewed the relevant source on both sides of the stack (versions below) and didn't find an obvious gap:

  • crates/tauri/src/webview/mod.rs: on_new_window stores the closure in new_window_handler; into_pending_webview unconditionally threads it into pending.new_window_handler — no branch that would skip this for WebviewUrl::External.
  • wry src/webview2/mod.rs: add_NewWindowRequested is registered unconditionally (not gated behind if let Some(new_window_req_handler) = ... the way the WebKitGTK backend does it); the handler checks if let Some(new_window_req_handler) = &new_window_req_handler { ... } else { args.SetHandled(true) }. Since a handler is configured in our case, it should reach the Some branch and invoke it via Self::dispatch_handler(hwnd, ...).

Given the else branch (args.SetHandled(true) with no further action) is exactly what silently swallows the event with no registered handler, this may hint at where a default app (no on_new_window at all) goes silent — but doesn't explain why a registered handler also never fires.

I don't yet have a minimal standalone reproduction (this was found inside a larger app — an unofficial WhatsApp Web desktop wrapper), but I can put one together if that would help triage. Happy to provide more detail (full on_navigation/on_download config, tauri.conf.json, etc.) on request.

Platform and versions

tauri: 2.11.2
tauri-runtime-wry: 2.11.2
wry: 0.55.1
webview2-com: 0.38
OS: Windows 11 Pro (10.0.26200)
WebView2 runtime: Evergreen (current)

App config relevant bits:

  • WebviewUrl::External("https://web.whatsapp.com/")
  • app.security.csp: null in tauri.conf.json (loading a remote origin we don't control)
  • withGlobalTauri: true

Additional context

Separately (possibly related, possibly not): the page's own CSP (connect-src sent by the remote server) blocks Tauri's http://ipc.localhost IPC channel entirely (Refused to connect to 'http://ipc.localhost/...' because it violates the Content Security Policy, then falls back to postMessage, which in our testing also never reaches Rust for any command). This is a documented/known limitation for remote pages with csp: null (e.g. #8476, #12835). I originally suspected that was the root cause of the link-opening issue too, and switched to on_new_window specifically because it should run entirely on the Rust/native side without touching page IPC — but as detailed above, it doesn't fire either, so I don't think the two issues share a root cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions