Skip to content

Commit

Permalink
Merge pull request #2033 from cwensley/curtis/mac-webkit-fix-crashing
Browse files Browse the repository at this point in the history
Mac: Fix WebView crash when subscribing to DocumentTitleChanged event
  • Loading branch information
cwensley authored Oct 1, 2021
2 parents 207caa0 + 5b2cbaf commit 68b5120
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 74 deletions.
15 changes: 14 additions & 1 deletion src/Eto.Mac/Forms/Controls/WKWebViewHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ protected override wk.WKWebView CreateControl()

public class EtoNavigationDelegate : wk.WKNavigationDelegate
{
public EtoNavigationDelegate()
{
}
public EtoNavigationDelegate(IntPtr handle)
: base(handle)
{
}

WeakReference handler;
public WKWebViewHandler Handler { get => handler?.Target as WKWebViewHandler; set => handler = new WeakReference(value); }

Expand Down Expand Up @@ -89,6 +97,7 @@ public override void DecidePolicy(wk.WKWebView webView, wk.WKNavigationAction na
}

}


protected override void Initialize()
{
Expand All @@ -110,7 +119,11 @@ public EtoWebView(WKWebViewHandler handler)
Handler = handler;
UIDelegate = new EtoUIDelegate { Handler = handler };
}


public EtoWebView(IntPtr handle)
: base(handle)
{
}
}

class PromptDialog : Dialog<bool>
Expand Down
64 changes: 21 additions & 43 deletions src/Eto.Mac/Forms/MacBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,13 @@ public class ObserverHelper : NSObject

public NSString KeyPath { get; set; }

WeakReference control;
public IntPtr ControlHandle { get; set; }

public NSObject Control { get { return (NSObject)(control != null ? control.Target : null); } set { control = new WeakReference(value); } }

WeakReference widget;

public Widget Widget { get { return (Widget)(widget != null ? widget.Target : null); } set { widget = new WeakReference(value); } }
public NSObject Control => Runtime.TryGetNSObject(ControlHandle);

WeakReference handler;

public object Handler { get { return handler != null ? handler.Target : null; } set { handler = new WeakReference(value); } }
public object Handler { get => handler?.Target; set => handler = new WeakReference(value); }

static readonly Selector selPerformAction = new Selector("performAction:");

Expand Down Expand Up @@ -100,27 +96,31 @@ public void AddToControl()
}
}

static readonly IntPtr selRelease_Handle = Selector.GetHandle("release");
static readonly IntPtr selRemoveObserverForKeyPath_Handle = Selector.GetHandle("removeObserver:forKeyPath:");

public void Remove()
{
var c = Control;
// we use the handle here to remove as it may have been GC'd but we still need to remove it!
if (isNotification)
{
NSNotificationCenter.DefaultCenter.RemoveObserver(this);
if (c != null)
c.DangerousRelease();
Messaging.void_objc_msgSend(ControlHandle, selRelease_Handle);

isNotification = false;
}
if (isControl && c != null && KeyPath != null)
if (isControl)
{
//Console.WriteLine ("{0}: 4. Removing observer! {1}, {2}", ((IRef)this.Handler).WidgetID, Handler.GetType (), Control.GetHashCode ());
c.RemoveObserver(this, KeyPath);
c.DangerousRelease();
Messaging.void_objc_msgSend_IntPtr_IntPtr(ControlHandle, selRemoveObserverForKeyPath_Handle, Handle, KeyPath.Handle);
Messaging.void_objc_msgSend(ControlHandle, selRelease_Handle);
isControl = false;
}
}

protected override void Dispose(bool disposing)
{
// this object has a finalizer so let's unsubscribe here
Remove();
base.Dispose(disposing);
}
Expand All @@ -136,12 +136,8 @@ public ObserverActionEventArgs(ObserverHelper observer, NSNotification notificat
this.Notification = notification;
}

public Widget Widget { get { return observer.Widget; } }

public object Handler { get { return observer.Handler; } }

public object Control { get { return observer.Control; } }

public NSString KeyPath { get { return observer.KeyPath; } }

public NSNotification Notification { get; private set; }
Expand Down Expand Up @@ -171,8 +167,8 @@ public static object GetHandler(object control)
}

public abstract class MacBase<TControl, TWidget, TCallback> : WidgetHandler<TControl, TWidget, TCallback>
where TControl: class
where TWidget: Widget
where TControl : class
where TWidget : Widget
{
protected override void Initialize()
{
Expand All @@ -193,12 +189,12 @@ protected override void Initialize()
public bool AddMethod(IntPtr selector, Delegate action, string arguments, object control)
{
var type = control.GetType();
#if OSX
#if OSX
if (!typeof(IMacControl).IsAssignableFrom(type))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Control '{0}' does not inherit from IMacControl", type));
if (((IMacControl)control).WeakHandler?.Target == null)
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Control '{0}' has a null handler", type));
#endif
#endif
var classHandle = Class.GetHandle(type);

return ObjCExtensions.AddMethod(classHandle, selector, action, arguments);
Expand All @@ -217,15 +213,12 @@ public NSObject AddObserver(NSString key, Action<ObserverActionEventArgs> action
if (observers == null)
{
observers = new List<ObserverHelper>();
// ensure we finalize to clean this up later
GC.ReRegisterForFinalize(this);
}
var observer = new ObserverHelper
{
Action = action,
KeyPath = key,
Control = control,
Widget = Widget,
ControlHandle = control.Handle,
Handler = this
};
observer.AddToNotificationCenter();
Expand All @@ -238,40 +231,25 @@ public void AddControlObserver(NSString key, Action<ObserverActionEventArgs> act
if (observers == null)
{
observers = new List<ObserverHelper>();
// ensure we finalize to clean this up later
GC.ReRegisterForFinalize(this);
}
var observer = new ObserverHelper
{
Action = action,
KeyPath = key,
Control = control,
Widget = Widget,
ControlHandle = control.Handle,
Handler = this
};
observer.AddToControl();
observers.Add(observer);
}

~MacBase()
{
//Console.WriteLine("Finalizing {0}", GetType());
// need to remove observers so we can GC the native object
Dispose(false);
}

public MacBase()
{
// finalizer is not actually needed until we add an observer.
GC.SuppressFinalize(this);
}

protected override void Dispose(bool disposing)
{
if (observers != null)
{
foreach (var observer in observers)
for (int i = 0; i < observers.Count; i++)
{
var observer = observers[i];
observer.Remove();
}
observers = null;
Expand Down
64 changes: 34 additions & 30 deletions src/Eto.Mac/Messaging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace Eto.Mac
{
public static class Messaging
{
const string LIBOBJC_DYLIB = "/usr/lib/libobjc.dylib";
public static readonly IntPtr AppKitHandle = Dlfcn.dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", 0);

public static readonly IntPtr CoreTextHandle = Dlfcn.dlopen("/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/CoreText", 0);
Expand All @@ -52,95 +53,98 @@ public static T GetNSObject<T>(IntPtr ptr)
return Runtime.GetNSObject<T>(ptr);
}

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern IntPtr IntPtr_objc_msgSendSuper_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern bool bool_objc_msgSendSuper_IntPtr_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern void void_objc_msgSendSuper_SizeF(IntPtr receiver, IntPtr selector, CGSize arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern nfloat nfloat_objc_msgSendSuper_nint(IntPtr receiver, IntPtr selector, nint segment);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern bool bool_objc_msgSendSuper(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern bool bool_objc_msgSendSuper_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern bool bool_objc_msgSendSuper_NSRange_IntPtr(IntPtr receiver, IntPtr sel, NSRange arg1, IntPtr arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern void void_objc_msgSendSuper_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern void void_objc_msgSendSuper_CGRect(IntPtr receiver, IntPtr selector, CGRect arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern void void_objc_msgSendSuper(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern bool bool_objc_msgSend(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern bool bool_objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern bool bool_objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern void void_objc_msgSend(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern void void_objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern void void_objc_msgSend_bool(IntPtr receiver, IntPtr selector, bool arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend_nuint(IntPtr receiver, IntPtr selector, nuint arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern nuint nuint_objc_msgSend(IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern void RectangleF_objc_msgSend_stret(out CGRect rect, IntPtr receiver, IntPtr selector);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend_stret")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend_stret")]
public static extern void RectangleF_objc_msgSend_stret_SizeF_int(out CGRect retval, IntPtr receiver, IntPtr selector, CGSize arg1, nint arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend_nuint_IntPtr_IntPtr_bool(IntPtr receiver, IntPtr selector, nuint mask, IntPtr untilDate, IntPtr mode, bool dequeue);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern void void_objc_msgSend_NSRange_CGPoint(IntPtr receiver, IntPtr selector, NSRange arg1, CGPoint arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern bool bool_objc_msgSend_NSRange_IntPtr(IntPtr receiver, IntPtr selector, NSRange arg1, IntPtr arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend_IntPtr(IntPtr reciever, IntPtr selector, IntPtr arg1);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend_IntPtr_IntPtr(IntPtr reciever, IntPtr selector, IntPtr arg1, IntPtr arg2);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern IntPtr IntPtr_objc_msgSend_IntPtr_IntPtr_IntPtr(IntPtr reciever, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public static extern CGSize CGSize_objc_msgSend_CGSize_IntPtr_IntPtr_UInt64_UInt64_Int64(IntPtr receiver, IntPtr selector, CGSize arg1, IntPtr arg2, IntPtr arg3, ulong arg4, ulong arg5, long arg6);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern void void_objc_msgSendSuper_IntPtr_IntPtr_nuint_IntPtr_CGAffineTransform_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, nuint arg3, IntPtr arg4, CGAffineTransform arg5, IntPtr arg6, IntPtr arg7);

[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
[DllImport(LIBOBJC_DYLIB, EntryPoint = "objc_msgSendSuper")]
public static extern IntPtr IntPtr_objc_msgSendSuper_IntPtr_IntPtr_IntPtr_ref_CGPoint(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3, ref CGPoint arg4);
[DllImport (LIBOBJC_DYLIB, EntryPoint="objc_msgSend")]
public extern static void void_objc_msgSend_IntPtr_IntPtr (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);

}
}

0 comments on commit 68b5120

Please sign in to comment.