Webview patches

This commit is contained in:
Ryan McGrath 2020-03-30 00:37:10 -07:00
parent ad54670ffd
commit ccaf61f56f
No known key found for this signature in database
GPG key ID: 811674B62B666830
3 changed files with 54 additions and 100 deletions

View file

@ -4,7 +4,6 @@
use std::ffi::c_void; use std::ffi::c_void;
use std::sync::Once; use std::sync::Once;
use std::rc::Rc;
use block::Block; use block::Block;
@ -50,11 +49,8 @@ extern fn on_message<T: WebViewDelegate>(this: &Object, _: Sel, _: id, script_me
unsafe { unsafe {
let name = NSString::wrap(msg_send![script_message, name]).to_str(); let name = NSString::wrap(msg_send![script_message, name]).to_str();
let body = NSString::wrap(msg_send![script_message, body]).to_str(); let body = NSString::wrap(msg_send![script_message, body]).to_str();
let d = delegate.borrow(); delegate.on_message(name, body);
(*d).on_message(name, body);
} }
Rc::into_raw(delegate);
} }
/// Fires when deciding a navigation policy - i.e, should something be allowed or not. /// Fires when deciding a navigation policy - i.e, should something be allowed or not.
@ -63,16 +59,10 @@ extern fn decide_policy_for_action<T: WebViewDelegate>(this: &Object, _: Sel, _:
let action = NavigationAction::new(action); let action = NavigationAction::new(action);
{ delegate.policy_for_navigation_action(action, |policy| unsafe {
let d = delegate.borrow(); let handler = handler as *const Block<(NSInteger,), c_void>;
(*handler).call((policy.into(),));
(*d).policy_for_navigation_action(action, |policy| unsafe { });
let handler = handler as *const Block<(NSInteger,), c_void>;
(*handler).call((policy.into(),));
});
}
Rc::into_raw(delegate);
} }
/// Fires when deciding a navigation policy - i.e, should something be allowed or not. /// Fires when deciding a navigation policy - i.e, should something be allowed or not.
@ -81,44 +71,32 @@ extern fn decide_policy_for_response<T: WebViewDelegate>(this: &Object, _: Sel,
let response = NavigationResponse::new(response); let response = NavigationResponse::new(response);
{ delegate.policy_for_navigation_response(response, |policy| unsafe {
let d = delegate.borrow(); let handler = handler as *const Block<(NSInteger,), c_void>;
(*handler).call((policy.into(),));
(*d).policy_for_navigation_response(response, |policy| unsafe { });
let handler = handler as *const Block<(NSInteger,), c_void>;
(*handler).call((policy.into(),));
});
}
Rc::into_raw(delegate);
} }
/// Fires when deciding a navigation policy - i.e, should something be allowed or not. /// Fires when deciding a navigation policy - i.e, should something be allowed or not.
extern fn run_open_panel<T: WebViewDelegate>(this: &Object, _: Sel, _: id, params: id, _: id, handler: usize) { extern fn run_open_panel<T: WebViewDelegate>(this: &Object, _: Sel, _: id, params: id, _: id, handler: usize) {
let delegate = load::<T>(this, WEBVIEW_DELEGATE_PTR); let delegate = load::<T>(this, WEBVIEW_DELEGATE_PTR);
{ delegate.run_open_panel(params.into(), move |urls| unsafe {
let d = delegate.borrow(); let handler = handler as *const Block<(id,), c_void>;
(*d).run_open_panel(params.into(), move |urls| unsafe { match urls {
let handler = handler as *const Block<(id,), c_void>; Some(u) => {
let nsurls: NSArray = u.iter().map(|s| {
let s = NSString::new(s);
msg_send![class!(NSURL), URLWithString:s.into_inner()]
}).collect::<Vec<id>>().into();
match urls { (*handler).call((nsurls.into_inner(),));
Some(u) => { },
let nsurls: NSArray = u.iter().map(|s| {
let s = NSString::new(s);
msg_send![class!(NSURL), URLWithString:s.into_inner()]
}).collect::<Vec<id>>().into();
(*handler).call((nsurls.into_inner(),)); None => { (*handler).call((nil,)); }
}, }
});
None => { (*handler).call((nil,)); }
}
});
}
Rc::into_raw(delegate);
} }
/// Called when a download has been initiated in the WebView, and when the navigation policy /// Called when a download has been initiated in the WebView, and when the navigation policy
@ -131,24 +109,18 @@ extern fn handle_download<T: WebViewDelegate>(this: &Object, _: Sel, download: i
let handler = handler as *const Block<(objc::runtime::BOOL, id), c_void>; let handler = handler as *const Block<(objc::runtime::BOOL, id), c_void>;
let filename = NSString::wrap(suggested_filename).to_str(); let filename = NSString::wrap(suggested_filename).to_str();
{ delegate.run_save_panel(filename, move |can_overwrite, path| unsafe {
let d = delegate.borrow(); if path.is_none() {
let _: () = msg_send![download, cancel];
}
(*d).run_save_panel(filename, move |can_overwrite, path| unsafe { let path = NSString::new(&path.unwrap());
if path.is_none() {
let _: () = msg_send![download, cancel]; (*handler).call((match can_overwrite {
} true => YES,
false => NO
let path = NSString::new(&path.unwrap()); }, path.into_inner()));
});
(*handler).call((match can_overwrite {
true => YES,
false => NO
}, path.into_inner()));
});
}
Rc::into_raw(delegate);
} }
/// Registers an `NSViewController` that we effectively turn into a `WebViewController`. Acts as /// Registers an `NSViewController` that we effectively turn into a `WebViewController`. Acts as

View file

@ -13,32 +13,32 @@
//! Apple does not ship `WKWebView` on tvOS, and as a result this control is not provided on that //! Apple does not ship `WKWebView` on tvOS, and as a result this control is not provided on that
//! platform. //! platform.
use std::rc::Rc;
use std::cell::RefCell;
use core_graphics::geometry::CGRect; use core_graphics::geometry::CGRect;
use objc_id::ShareId; use objc_id::ShareId;
use objc::runtime::Object; use objc::runtime::Object;
use objc::{class, msg_send, sel, sel_impl}; use objc::{class, msg_send, sel, sel_impl};
use crate::foundation::{id, YES, NO, NSString}; use crate::foundation::{id, nil, YES, NO, NSString};
use crate::geometry::Rect; use crate::geometry::Rect;
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension}; use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
pub mod actions; mod actions;
pub mod enums; pub use actions::*;
mod config;
pub use config::WebViewConfig;
mod enums;
pub use enums::*;
pub(crate) mod class; pub(crate) mod class;
use class::{register_webview_class, register_webview_delegate_class}; use class::{register_webview_class, register_webview_delegate_class};
//pub(crate) mod process_pool; //pub(crate) mod process_pool;
pub mod traits; mod traits;
pub use traits::WebViewDelegate; pub use traits::WebViewDelegate;
pub mod config;
pub use config::WebViewConfig;
pub(crate) static WEBVIEW_DELEGATE_PTR: &str = "rstWebViewDelegatePtr"; pub(crate) static WEBVIEW_DELEGATE_PTR: &str = "rstWebViewDelegatePtr";
fn allocate_webview( fn allocate_webview(
@ -87,12 +87,8 @@ pub struct WebView<T = ()> {
/// we do so. /// we do so.
pub objc_delegate: Option<ShareId<Object>>, pub objc_delegate: Option<ShareId<Object>>,
/// An internal callback pointer that we use in delegate loopbacks. Default implementations
/// don't require this.
pub(crate) internal_callback_ptr: Option<*const RefCell<T>>,
/// A pointer to the delegate for this view. /// A pointer to the delegate for this view.
pub delegate: Option<Rc<RefCell<T>>>, pub delegate: Option<Box<T>>,
/// A pointer to the Objective-C runtime top layout constraint. /// A pointer to the Objective-C runtime top layout constraint.
pub top: LayoutAnchorY, pub top: LayoutAnchorY,
@ -130,7 +126,6 @@ impl WebView {
let view = allocate_webview(config, None); let view = allocate_webview(config, None);
WebView { WebView {
internal_callback_ptr: None,
delegate: None, delegate: None,
objc_delegate: None, objc_delegate: None,
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
@ -150,23 +145,18 @@ impl<T> WebView<T> where T: WebViewDelegate + 'static {
/// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events /// Initializes a new WebView with a given `WebViewDelegate`. This enables you to respond to events
/// and customize the view as a module, similar to class-based systems. /// and customize the view as a module, similar to class-based systems.
pub fn with(config: WebViewConfig, delegate: T) -> WebView<T> { pub fn with(config: WebViewConfig, delegate: T) -> WebView<T> {
let delegate = Rc::new(RefCell::new(delegate)); let delegate = Box::new(delegate);
let internal_callback_ptr = {
let cloned = Rc::clone(&delegate);
Rc::into_raw(cloned)
};
let objc_delegate = unsafe { let objc_delegate = unsafe {
let objc_delegate: id = msg_send![register_webview_delegate_class::<T>(), new]; let objc_delegate: id = msg_send![register_webview_delegate_class::<T>(), new];
(&mut *objc_delegate).set_ivar(WEBVIEW_DELEGATE_PTR, internal_callback_ptr as usize); let ptr: *const T = &*delegate;
(&mut *objc_delegate).set_ivar(WEBVIEW_DELEGATE_PTR, ptr as usize);
ShareId::from_ptr(objc_delegate) ShareId::from_ptr(objc_delegate)
}; };
let view = allocate_webview(config, Some(&objc_delegate)); let view = allocate_webview(config, Some(&objc_delegate));
let mut view = WebView { let mut view = WebView {
internal_callback_ptr: Some(internal_callback_ptr),
delegate: None, delegate: None,
objc_delegate: Some(objc_delegate), objc_delegate: Some(objc_delegate),
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }), top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
@ -180,11 +170,7 @@ impl<T> WebView<T> where T: WebViewDelegate + 'static {
objc: unsafe { ShareId::from_ptr(view) }, objc: unsafe { ShareId::from_ptr(view) },
}; };
{ &delegate.did_load(view.clone_as_handle());
let mut delegate = delegate.borrow_mut();
(*delegate).did_load(view.clone_as_handle());
}
view.delegate = Some(delegate); view.delegate = Some(delegate);
view view
} }
@ -197,7 +183,6 @@ impl<T> WebView<T> {
/// delegate - the `View` is the only true holder of those. /// delegate - the `View` is the only true holder of those.
pub(crate) fn clone_as_handle(&self) -> WebView { pub(crate) fn clone_as_handle(&self) -> WebView {
WebView { WebView {
internal_callback_ptr: None,
delegate: None, delegate: None,
top: self.top.clone(), top: self.top.clone(),
leading: self.leading.clone(), leading: self.leading.clone(),
@ -245,14 +230,11 @@ impl<T> std::fmt::Debug for WebView<T> {
impl<T> Drop for WebView<T> { impl<T> Drop for WebView<T> {
/// A bit of extra cleanup for delegate callback pointers. /// A bit of extra cleanup for delegate callback pointers.
fn drop(&mut self) { fn drop(&mut self) {
/*println!("... {}", self.delegate.is_some()); if self.delegate.is_some() {
if let Some(delegate) = &self.delegate { unsafe {
println!("Strong count: {}", Rc::strong_count(&delegate)); let _: () = msg_send![&*self.objc, setNavigationDelegate:nil];
if Rc::strong_count(&delegate) == 1 { let _: () = msg_send![&*self.objc, setUIDelegate:nil];
let _ = unsafe {
Rc::from_raw(self.internal_callback_ptr.unwrap())
};
} }
}*/ }
} }
} }

View file

@ -11,7 +11,7 @@ pub trait WebViewDelegate {
/// Called when the View is ready to work with. You're passed a `ViewHandle` - this is safe to /// Called when the View is ready to work with. You're passed a `ViewHandle` - this is safe to
/// store and use repeatedly, but it's not thread safe - any UI calls must be made from the /// store and use repeatedly, but it's not thread safe - any UI calls must be made from the
/// main thread! /// main thread!
fn did_load(&mut self, _webview: WebView) {} fn did_load(&self, _webview: WebView) {}
/// Called when this is about to be added to the view heirarchy. /// Called when this is about to be added to the view heirarchy.
fn will_appear(&self) {} fn will_appear(&self) {}