Using native Cocoa WebKit for System.Windows.Forms.WebBrowser on Mac OS X

Alex Shulgin <[email protected]>
Newsgroups gmane.comp.gnome.winforms
Message-ID <[email protected]>
Hi,

Recently I've been struggling to bring up native (Cocoa-based) WebKit as 
WinForms WebBrowser control on OS X.

The approach I've used works to some degree, but has problems for which 
I need your help to solve.

Here's what I've come up with:

* I've found out that there's an API to integrate Cocoa views into 
Carbon views (HICocoaViewCreate, HICocoaViewSetView et al).

For this to work with WinForms we'd need to subclass 
"com.apple.HICocoaView", otherwise system events won't be translated to 
WinForms messages.

I have taken a shortcut and added new internal field to CreateParams 
class and used it in CreateWindow to select special subclass name.

To support this on the WebBrowser control side, I've patched 
WebBrowserBase.cs to use CreateParams.internal_param when running on Mac OS.

This might look a bit clumsy, but it works.  See hicocoaview-subclass.patch.

* After that I've noticed that the actual browser engine to use is 
chosen in Mono.WebBrowser/Manager.cs.  I've patched it to use alternate 
implementation on Mac OS by default.  See Mono.WebBrowser.Manager.patch.

* Finally, I've added mono-cocoa-webkit.dll modeled after 
mono-webkit.dll to provide minimum required functionality: create WebKit 
& navigate to URL.

I've used Monobjc bridge to create WebKit instance for simplicity: 
http://monobjc.net/

See attached sources archive for details.  You can use make; sudo make 
install.  Also, you'll need to install Monobjc dlls to gac too for this 
to work:

e.g. in ~/src/Monobjc-2.0.357.0/dist/2.0/
$ sudo gacutil -i Monobjc.dll
$ sudo gacutil -i Monobjc.Cocoa.dll
$ sudo gacutil -i Monobjc.WebKit.dll


With all this I was able to use WinForms WebBrowser with native WebKit 
sitting inside it. :) But there are some problems. :(

With this approach the WebBrowser control is grabbing focus after a 
click on it and extra click on some other control is required to make it 
lose the focus.

Sample code to test is attached in SimpleBrowser.zip.

I've compiled System.Windows.Forms.dll with most of debugging macros 
defined, and it all boils down to the fact that during the click 
WebBrowser control is receiving button down event, but no button up.

The relevant part of debugging info is like this:

NativeWindow.cs: Message WM_MOUSEFIRST, result 0
CarbonInternal.EventHandler: callref=BFFFED20, eventref=9D9C80, 
handle=9BD070, klass=1836021107, kind=1
CarbonInternal.MouseHandler: callref=BFFFED20, eventref=9D9C80, 
handle=9BD070, kind=1
NativeWindow.cs (10227120, WM_LBUTTONDOWN, 1, 7405631): result 0
Control 'System.Windows.Forms.WebBrowser' (9C0DB0) received message 
msg=0x201 (WM_LBUTTONDOWN) hwnd=0x9c0db0 wparam=0x1 lparam=0x71003f 
result=0x0
IsEnabled('System.Windows.Forms.WebBrowser' (9C0DB0)): Called, Result=True
set_Capture: 'System.Windows.Forms.WebBrowser' (9C0DB0) value=True 
is_captured=False IsHandleCreated=True
GrabWindow('System.Windows.Forms.WebBrowser' (9C0DB0), '' (0)): Called
NativeWindow.cs: Message WM_LBUTTONDOWN, result 0

Moreover, after the click inside the browser it seems that it stops 
receiving Carbon events altogether, so there's no chance to translate 
them into WinForms messages...

So, basically I'm stuck here.  Any pointers & suggestions are much 
appreciated!

--
Cheers,
Alex

_______________________________________________
Mono-winforms-list maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-winforms-list
mono-cocoa-webkit-0.1.0.tar.gz (application/x-gzip, 3.2 KB) - not displayed
Mono.WebBrowser.Manager.patch (text/x-patch, 1.6 KB)
Index: Manager.cs
===================================================================
--- Manager.cs	(revision 136302)
+++ Manager.cs	(working copy)
@@ -25,6 +25,7 @@
 
 using System;
 using System.Reflection;
+using System.Runtime.InteropServices;
 
 namespace Mono.WebBrowser
 {
@@ -39,8 +40,17 @@
 		{
 			string browserEngine = Environment.GetEnvironmentVariable ("MONO_BROWSER_ENGINE");
 
-#if NET_2_0			
-			if (browserEngine == "webkit") {
+#if NET_2_0
+			if (RunningOnMacOSX) {
+				if (browserEngine == null || browserEngine == "webkit") {
+					try {
+						Assembly assembly = Assembly.LoadWithPartialName ("mono-cocoa-webkit");
+						return (IWebBrowser) assembly.CreateInstance ("Mono.CocoaWebKit.WebBrowser");
+					} catch {
+						browserEngine = null;
+					}
+				}
+			} else if (browserEngine == "webkit") {
 				Assembly ass;
 				try {
 					ass = Assembly.LoadWithPartialName ("mono-webkit");
@@ -57,6 +67,31 @@
 			throw new Exception (Mono.WebBrowser.Exception.ErrorCodes.EngineNotSupported, browserEngine);
 		}
 
-		
+		// OS detection hackery grabbed from System.Windows.Forms/XplatUI.cs
+		static bool RunningOnUnix {
+			get {
+				int p = (int) Environment.OSVersion.Platform;
+				return (p == 4 || p == 6 || p == 128);
+			}
+		}
+
+		static bool RunningOnMacOSX {
+			get {
+				bool ret = false;
+				if (RunningOnUnix) {
+					IntPtr buf = Marshal.AllocHGlobal (8192);
+					if (uname (buf) == 0) {
+						string os = Marshal.PtrToStringAnsi (buf);
+						if (os == "Darwin")
+							ret = true;
+					}
+					Marshal.FreeHGlobal (buf);
+				}
+				return ret;
+			}
+		}
+
+		[DllImport ("libc")]
+		static extern int uname (IntPtr buf);
 	}
 }
SimpleBrowser.zip (application/zip, 2.7 KB) - not displayed
hicocoaview-subclass.patch (text/x-patch, 3.6 KB)
Index: XplatUICarbon.cs
===================================================================
--- XplatUICarbon.cs	(revision 136302)
+++ XplatUICarbon.cs	(working copy)
@@ -244,6 +244,7 @@
 			SetFrontProcess (ref psn);
 
 			HIObjectRegisterSubclass (__CFStringMakeConstantString ("com.novell.mwfview"), __CFStringMakeConstantString ("com.apple.hiview"), 0, Carbon.EventHandler.EventHandlerDelegate, (uint)Carbon.EventHandler.HIObjectEvents.Length, Carbon.EventHandler.HIObjectEvents, IntPtr.Zero, ref Subclass);
+			HIObjectRegisterSubclass (__CFStringMakeConstantString ("com.novell.mwfhicocoaview"), __CFStringMakeConstantString ("com.apple.HICocoaView"), 0, Carbon.EventHandler.EventHandlerDelegate, (uint)Carbon.EventHandler.HIObjectEvents.Length, Carbon.EventHandler.HIObjectEvents, IntPtr.Zero, ref Subclass);
 
 			Carbon.EventHandler.InstallApplicationHandler ();
 
@@ -996,7 +997,7 @@
 			}
 
 			HIObjectCreate (__CFStringMakeConstantString ("com.novell.mwfview"), 0, ref WholeWindow);
-			HIObjectCreate (__CFStringMakeConstantString ("com.novell.mwfview"), 0, ref ClientWindow);
+			HIObjectCreate (__CFStringMakeConstantString (cp.internal_param != null ? (string) cp.internal_param : "com.novell.mwfview"), 0, ref ClientWindow);
 
 			Carbon.EventHandler.InstallControlHandler (WholeWindow);
 			Carbon.EventHandler.InstallControlHandler (ClientWindow);
Index: CreateParams.cs
===================================================================
--- CreateParams.cs	(revision 136302)
+++ CreateParams.cs	(working copy)
@@ -45,6 +45,7 @@
 		private IntPtr	parent;
 		internal Menu	menu;
 		internal Control	control;
+		internal object	internal_param;
 		#endregion 	// Local variables
 
 		#region Public Constructors
Index: WebBrowserBase.cs
===================================================================
--- WebBrowserBase.cs	(revision 136302)
+++ WebBrowserBase.cs	(working copy)
@@ -304,9 +304,80 @@
 		{
 			base.WndProc (ref m);
 		}
+
+		protected override CreateParams CreateParams {
+			get {
+				CreateParams cp = base.CreateParams;
+				if (UsingCocoaWebKit) {
+					cp.internal_param = "com.novell.mwfhicocoaview";
+				}
+				return cp;
+			}
+		}
+
+		protected override void OnPaint (PaintEventArgs e)
+		{
+			if (UsingCocoaWebKit)
+				return;
+
+			base.OnPaint (e);
+		}
 		
+		protected override void OnPaintBackground (PaintEventArgs e)
+		{
+			if (UsingCocoaWebKit)
+				return;
+
+			base.OnPaintBackground (e);
+		}
+		
 		#endregion
 
+		// OS detection hackery grabbed from System.Windows.Forms/XplatUI.cs
+		static bool? using_cocoa_webkit = null;
+
+		static bool UsingCocoaWebKit {
+			get {
+				if (using_cocoa_webkit == null) {
+					using_cocoa_webkit = false;
+
+					if (RunningOnMacOSX) {
+						string browserEngine = Environment.GetEnvironmentVariable ("MONO_BROWSER_ENGINE");
+						if (browserEngine == null || browserEngine == "webkit")
+							using_cocoa_webkit = true;
+					}
+				}
+
+				return (bool) using_cocoa_webkit;
+			}
+		}
+
+		static bool RunningOnUnix {
+			get {
+				int p = (int) Environment.OSVersion.Platform;
+				return (p == 4 || p == 6 || p == 128);
+			}
+		}
+
+		static bool RunningOnMacOSX {
+			get {
+				bool ret = false;
+				if (RunningOnUnix) {
+					IntPtr buf = Marshal.AllocHGlobal (8192);
+					if (uname (buf) == 0) {
+						string os = Marshal.PtrToStringAnsi (buf);
+						if (os == "Darwin")
+							ret = true;
+					}
+					Marshal.FreeHGlobal (buf);
+				}
+				return ret;
+			}
+		}
+
+		[DllImport ("libc")]
+		static extern int uname (IntPtr buf);
+
 		#region Internal Properties
 
 		enum State
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.