Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changes/macos-ipc-handlers-undefined.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wry": patch
---

On macOS, a Tauri v2 app can lose the WebKit message handler used by Tauri IPC after a popup opened via window.open is handled with tauri::webview::NewWindowResponse::Create and then closed.

After this happens, the opener/main window still has Tauri's JavaScript initialization objects, but later invoke() calls fail because window.webkit.messageHandlers becomes undefined which means it is no longer available.

Fixed by removing the IPC message handler only if it is registered/owned by the popup.
17 changes: 12 additions & 5 deletions src/wkwebview/class/wry_web_view_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use std::{ffi::CStr, panic::AssertUnwindSafe};
use std::{cell::Cell, ffi::CStr, panic::AssertUnwindSafe};

use http::Request;
use objc2::{
Expand All @@ -19,6 +19,7 @@ pub const IPC_MESSAGE_HANDLER_NAME: &str = "ipc";
pub struct WryWebViewDelegateIvars {
pub controller: Retained<WKUserContentController>,
pub ipc_handler: Box<dyn Fn(Request<String>)>,
pub registered_ipc_handler: Cell<bool>,
}

define_class!(
Expand Down Expand Up @@ -85,20 +86,26 @@ impl WryWebViewDelegate {
.set_ivars(WryWebViewDelegateIvars {
ipc_handler,
controller,
registered_ipc_handler: Cell::new(false),
});

let delegate: Retained<Self> = unsafe { msg_send![super(delegate), init] };

let proto_delegate = ProtocolObject::from_ref(&*delegate);
unsafe {
let did_register_ipc_handler = unsafe {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what if you close the first window but kept the one from window.open? (when the first window drops, it will still remove the handler for everyone on the same content world?)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very good point. According to my knowledge, it will, because the message handler is registered successfully by the first window(Who registered it, who removes it). But I didn't make a test about it. I'll make a test tomorrow when I have my computer and give you response.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answer is yes according to the test. I got this exception in the other windows after the first window is closed.
TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers.ipc.postMessage')

// this will increase the retain count of the delegate
let _res = objc2::exception::catch(AssertUnwindSafe(|| {
objc2::exception::catch(AssertUnwindSafe(|| {
delegate
.ivars()
.controller
.addScriptMessageHandler_name(proto_delegate, ns_string!(IPC_MESSAGE_HANDLER_NAME));
}));
}
}))
.is_ok()
};
delegate
.ivars()
.registered_ipc_handler
.set(did_register_ipc_handler);

delegate
}
Expand Down
14 changes: 8 additions & 6 deletions src/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1400,12 +1400,14 @@ impl Drop for InnerWebView {
// We need to drop handler closures here
unsafe {
if let Some(ipc_handler) = self.ipc_handler_delegate.take() {
let ipc = ns_string!(IPC_MESSAGE_HANDLER_NAME);
// this will decrease the retain count of the ipc handler and trigger the drop
ipc_handler
.ivars()
.controller
.removeScriptMessageHandlerForName(ipc);
if ipc_handler.ivars().registered_ipc_handler.get() {
let ipc = ns_string!(IPC_MESSAGE_HANDLER_NAME);
// this will decrease the retain count of the ipc handler and trigger the drop
ipc_handler
.ivars()
.controller
.removeScriptMessageHandlerForName(ipc);
}
}

// Remove webview from window's NSView before dropping.
Expand Down
Loading