Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(macos): Prevent NSExceptions when dropping a webview with registered protocols #1215

Merged
merged 5 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .changes/macos-drop-panic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": patch
---

On macOS, prevent NSExceptions and invalid memory access panics when dropping the WebView while custom protocols handlers may still be running.
30 changes: 30 additions & 0 deletions src/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use cocoa::{
foundation::{NSDictionary, NSFastEnumeration, NSInteger},
};
use dpi::{LogicalPosition, LogicalSize};
use once_cell::sync::Lazy;
use raw_window_handle::{HasWindowHandle, RawWindowHandle};

use std::{
Expand Down Expand Up @@ -71,6 +72,8 @@ const ACCEPT_FIRST_MOUSE: &str = "accept_first_mouse";

const NS_JSON_WRITING_FRAGMENTS_ALLOWED: u64 = 4;

static WEBVIEW_IDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(Default::default);
FabianLars marked this conversation as resolved.
Show resolved Hide resolved

pub(crate) struct InnerWebView {
pub webview: id,
pub manager: id,
Expand All @@ -86,6 +89,7 @@ pub(crate) struct InnerWebView {
drag_drop_ptr: *mut Box<dyn Fn(crate::DragDropEvent) -> bool>,
download_delegate: id,
protocol_ptrs: Vec<*mut Box<dyn Fn(Request<Vec<u8>>, RequestAsyncResponder)>>,
webview_id: usize,
}

impl InnerWebView {
Expand Down Expand Up @@ -172,6 +176,8 @@ impl InnerWebView {
#[cfg(feature = "tracing")]
let span = tracing::info_span!("wry::custom_protocol::handle", uri = tracing::field::Empty)
.entered();
let webview_id = *this.get_ivar::<usize>("webview_id");

let function = this.get_ivar::<*mut c_void>("function");
if !function.is_null() {
let function =
Expand Down Expand Up @@ -271,14 +277,25 @@ impl InnerWebView {

let urlresponse: id = msg_send![class!(NSHTTPURLResponse), alloc];
let response: id = msg_send![urlresponse, initWithURL:url statusCode: wanted_status_code HTTPVersion:NSString::new(&wanted_version) headerFields:headers];
if !WEBVIEW_IDS.lock().unwrap().contains(&webview_id) {
return;
}
let () = msg_send![task, didReceiveResponse: response];

// Send data
let bytes = content.as_ptr() as *mut c_void;
let data: id = msg_send![class!(NSData), alloc];
let data: id = msg_send![data, initWithBytesNoCopy:bytes length:content.len() freeWhenDone: if content.len() == 0 { NO } else { YES }];

if !WEBVIEW_IDS.lock().unwrap().contains(&webview_id) {
return;
}
let () = msg_send![task, didReceiveData: data];

// Finish
if !WEBVIEW_IDS.lock().unwrap().contains(&webview_id) {
return;
}
let () = msg_send![task, didFinish];
},
);
Expand All @@ -299,6 +316,11 @@ impl InnerWebView {
}
extern "C" fn stop_task(_: &Object, _: Sel, _webview: id, _task: id) {}

let mut wv_ids = WEBVIEW_IDS.lock().unwrap();
let webview_id = wv_ids.last().unwrap_or(&0) + 1;
wv_ids.push(webview_id);
drop(wv_ids);

// Safety: objc runtime calls are unsafe
unsafe {
// Config and custom protocol
Expand All @@ -318,6 +340,7 @@ impl InnerWebView {
let cls = match cls {
Some(mut cls) => {
cls.add_ivar::<*mut c_void>("function");
cls.add_ivar::<usize>("webview_id");
cls.add_method(
sel!(webView:startURLSchemeTask:),
start_task as extern "C" fn(&Object, Sel, id, id),
Expand All @@ -335,6 +358,7 @@ impl InnerWebView {
protocol_ptrs.push(function);

(*handler).set_ivar("function", function as *mut _ as *mut c_void);
(*handler).set_ivar("webview_id", webview_id);
let () = msg_send![config, setURLSchemeHandler:handler forURLScheme:NSString::new(&name)];
}

Expand Down Expand Up @@ -878,6 +902,7 @@ impl InnerWebView {
download_delegate,
protocol_ptrs,
is_child,
webview_id,
};

// Initialize scripts
Expand Down Expand Up @@ -1245,6 +1270,11 @@ pub fn platform_webview_version() -> Result<String> {

impl Drop for InnerWebView {
fn drop(&mut self) {
WEBVIEW_IDS
.lock()
.unwrap()
.retain(|i| i != &self.webview_id);
FabianLars marked this conversation as resolved.
Show resolved Hide resolved

// We need to drop handler closures here
unsafe {
if !self.ipc_handler_ptr.is_null() {
Expand Down
Loading