Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ static ASWebAuthenticationPresentationContextProviding()

var delegateClass = AllocateClassPair("ManagedASWebAuthenticationPresentationContextProviding");

if (Libobjc.objc_getProtocol("ASWebAuthenticationPresentationContextProviding") is var protocol and > 0)
{
AddProtocol(delegateClass, protocol);
}
var presentationAnchorSel = Libobjc.sel_getUid("presentationAnchorForWebAuthenticationSession:");

AddProtocol(delegateClass, "ASWebAuthenticationPresentationContextProviding",
new ProtocolMethod(presentationAnchorSel, "@@:@", true));

AddMethod(delegateClass,
Libobjc.sel_getUid("presentationAnchorForWebAuthenticationSession:"),
presentationAnchorSel,
new IntPtr(s_presentationAnchorForWebAuthenticationSession),
"@@:@");

Expand Down
8 changes: 8 additions & 0 deletions src/Avalonia.Controls.WebView.Core/Macios/Interop/Libobjc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ internal static unsafe partial class Libobjc

[LibraryImport(libobjc, StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr objc_getProtocol(string selector);
[LibraryImport(libobjc, StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr objc_allocateProtocol(string name);
[LibraryImport(libobjc)]
public static partial void objc_registerProtocol(IntPtr protocol);
[LibraryImport(libobjc)]
public static partial void protocol_addProtocol(IntPtr protocol, IntPtr addition);
[LibraryImport(libobjc, StringMarshalling = StringMarshalling.Utf8)]
public static partial void protocol_addMethodDescription(IntPtr protocol, IntPtr selector, string types, byte isRequiredMethod, byte isInstanceMethod);
[LibraryImport(libobjc)]
public static partial byte class_addProtocol(IntPtr basePtr, IntPtr protocol);
[LibraryImport(libobjc, StringMarshalling = StringMarshalling.Utf8)]
Expand Down
47 changes: 47 additions & 0 deletions src/Avalonia.Controls.WebView.Core/Macios/Interop/NSObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,51 @@ protected static void AddProtocol(IntPtr classHandle, IntPtr protocol)
}
}

/// <summary>
/// Adds the protocol to the class.
/// If the runtime doesn't know the protocol yet, registers one with the same name and the given methods.
/// </summary>
/// <remarks>
/// objc_getProtocol only finds protocols that some loaded image references.
/// On iOS 26, WebKit protocols are not always registered, and WebKit rejects handlers that don't conform.
/// The runtime checks conformance by protocol name, so a protocol with the same name is enough for conformsToProtocol:.
/// Method descriptions are still added, so code that inspects the protocol sees the same methods as in the original one.
/// </remarks>
protected static void AddProtocol(IntPtr classHandle, string protocolName, params ReadOnlySpan<ProtocolMethod> methods)
{
var protocol = Libobjc.objc_getProtocol(protocolName);
if (protocol == IntPtr.Zero)
{
protocol = Libobjc.objc_allocateProtocol(protocolName);
if (protocol == IntPtr.Zero)
{
// Another thread or image registered it in the meantime.
protocol = Libobjc.objc_getProtocol(protocolName);
}
else
{
if (Libobjc.objc_getProtocol("NSObject") is var nsObjectProtocol and not 0)
{
Libobjc.protocol_addProtocol(protocol, nsObjectProtocol);
}
foreach (var method in methods)
{
Libobjc.protocol_addMethodDescription(protocol, method.Selector, method.Types,
method.Required ? (byte)1 : (byte)0, 1);
}
Libobjc.objc_registerProtocol(protocol);
}
}

if (protocol == IntPtr.Zero)
{
Logger.TryGet(LogEventLevel.Warning, "WebView")?.Log(null, "Failed to resolve {Protocol} protocol", protocolName);
return;
}

AddProtocol(classHandle, protocol);
}

protected static void AddMethod(IntPtr classHandle, IntPtr selector, IntPtr methodHandler, string types)
{
if (classHandle == IntPtr.Zero)
Expand Down Expand Up @@ -266,6 +311,8 @@ protected internal unsafe IntPtr GetSuperRef()
return new IntPtr(_superRef);
}

protected readonly record struct ProtocolMethod(IntPtr Selector, string Types, bool Required);

[StructLayout (LayoutKind.Sequential)]
private struct ObjcSuper {
public IntPtr Handle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ static WKNavigationDelegate()
{
var delegateClass = AllocateClassPair("ManagedWKNavigationDelegate");

if (Libobjc.objc_getProtocol("WKNavigationDelegate") is var protocol and > 0)
{
AddProtocol(delegateClass, protocol);
}

var willPresentNotificationSel = Libobjc.sel_getUid("webView:didFinishNavigation:");
var didReceiveNotificationResponse = Libobjc.sel_getUid("webView:decidePolicyForNavigationAction:decisionHandler:");

// All WKNavigationDelegate methods are optional.
AddProtocol(delegateClass, "WKNavigationDelegate",
new ProtocolMethod(willPresentNotificationSel, "v@:@@", false),
new ProtocolMethod(didReceiveNotificationResponse, "v@:@@@?", false));

AddMethod(delegateClass, willPresentNotificationSel, new IntPtr(s_willPresentNotification), "v@:@@");

var didReceiveNotificationResponse = Libobjc.sel_getUid("webView:decidePolicyForNavigationAction:decisionHandler:");
AddMethod(delegateClass, didReceiveNotificationResponse, new IntPtr(s_decidePolicyForNavigationAction), "v@:@@@");

RegisterManagedMembers(delegateClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ static WKScriptMessageHandler()
{
var delegateClass = AllocateClassPair("ManagedWKScriptMessageHandler");

if (Libobjc.objc_getProtocol("WKScriptMessageHandler") is var protocol and > 0)
{
AddProtocol(delegateClass, protocol);
}
var didReceiveScriptMessageSel = Libobjc.sel_getUid("userContentController:didReceiveScriptMessage:");

AddProtocol(delegateClass, "WKScriptMessageHandler",
new ProtocolMethod(didReceiveScriptMessageSel, "v@:@@", true));

var willPresentNotificationSel = Libobjc.sel_getUid("userContentController:didReceiveScriptMessage:");
AddMethod(delegateClass, willPresentNotificationSel, new IntPtr(s_didReceiveScriptMessage), "v@:@@");
AddMethod(delegateClass, didReceiveScriptMessageSel, new IntPtr(s_didReceiveScriptMessage), "v@:@@");

RegisterManagedMembers(delegateClass);

Expand Down
Loading