#if UNITY_EDITOR_OSX using System; using System.Runtime.InteropServices; using UnityEngine; using UnityTcp.Editor.Helpers; namespace Cn.Tuanjie.Codely.Editor { /// /// macOS WKWebView integration wrapper /// Provides a platform-agnostic interface for WebView operations /// public class MacOSWebViewBridge { #if UNITY_EDITOR_OSX #region Native Plugin Imports [DllImport("WebViewPlugin")] private static extern IntPtr WebView_Create(IntPtr guiViewPtr, string url, float x, float y, float w, float h); [DllImport("WebViewPlugin")] private static extern IntPtr WebView_CreateByWindowTitle(string windowTitle, string url, float x, float y, float w, float h); [DllImport("WebViewPlugin")] private static extern void WebView_Destroy(IntPtr handle); [DllImport("WebViewPlugin")] private static extern void WebView_UpdateFrame(IntPtr handle, float x, float y, float w, float h); [DllImport("WebViewPlugin")] private static extern void WebView_UpdateParentAndFrame(IntPtr handle, IntPtr guiViewPtr, float x, float y, float w, float h); [DllImport("WebViewPlugin")] private static extern void WebView_LoadURL(IntPtr handle, string url); [DllImport("WebViewPlugin")] private static extern void WebView_GoBack(IntPtr handle); [DllImport("WebViewPlugin")] private static extern void WebView_GoForward(IntPtr handle); [DllImport("WebViewPlugin")] private static extern void WebView_Reload(IntPtr handle); [DllImport("WebViewPlugin")] private static extern void WebView_ExecuteJavaScript(IntPtr handle, string script); [DllImport("WebViewPlugin")] private static extern void WebView_SetHidden(IntPtr handle, bool hidden); [DllImport("WebViewPlugin")] private static extern void WebView_ShowInspector(IntPtr handle); [DllImport("WebViewPlugin")] private static extern void WebView_Focus(IntPtr handle); [DllImport("WebViewPlugin")] private static extern bool WebView_ValidateHandle(IntPtr handle); [DllImport("WebViewPlugin")] private static extern bool WebView_HasValidContent(IntPtr handle); // Unity Drag Gateway imports (now in WebViewPlugin) [DllImport("WebViewPlugin")] private static extern IntPtr InstallUnityDragGateway(IntPtr wkWebViewPtr, IntPtr guiRenderViewPtr); [DllImport("WebViewPlugin")] private static extern void UninstallUnityDragGateway(IntPtr gatewayPtr); [DllImport("WebViewPlugin")] private static extern void UpdateDragGatewayFrame(IntPtr gatewayPtr, float x, float y, float w, float h); #endregion #endif private IntPtr _webViewHandle = IntPtr.Zero; private bool _isInitialized = false; private IntPtr _dragGatewayHandle = IntPtr.Zero; public bool IsInitialized => _isInitialized; public IntPtr Handle => _webViewHandle; public bool RestoreFromHandle(IntPtr handle) { #if UNITY_EDITOR_OSX if (handle == IntPtr.Zero) { return false; } try { // Verify the handle is still valid using the native registry if (!WebView_ValidateHandle(handle)) { return false; } _webViewHandle = handle; _isInitialized = true; return true; } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Exception while restoring handle: {ex.Message}"); return false; } #else return false; #endif } /// /// Check if the WebView has valid content loaded (not blank/white screen) /// public bool HasValidContent() { #if UNITY_EDITOR_OSX if (_webViewHandle == IntPtr.Zero || !_isInitialized) { return false; } try { return WebView_HasValidContent(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Exception checking content validity: {ex.Message}"); return false; } #else return false; #endif } /// /// Create and initialize the WebView using window title (recommended) /// public bool CreateByWindowTitle(string windowTitle, string url, float x, float y, float w, float h) { #if UNITY_EDITOR_OSX try { if (_isInitialized) { CodelyLogger.LogWarning("MacOSWebViewBridge: WebView already initialized"); return false; } if (string.IsNullOrEmpty(windowTitle)) { CodelyLogger.LogError("MacOSWebViewBridge: Invalid window title"); return false; } _webViewHandle = WebView_CreateByWindowTitle(windowTitle, url, x, y, w, h); if (_webViewHandle != IntPtr.Zero) { _isInitialized = true; return true; } else { CodelyLogger.LogError($"MacOSWebViewBridge: Failed to create WebView for window '{windowTitle}' (returned null handle)"); return false; } } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Exception creating WebView: {ex.Message}\n{ex.StackTrace}"); return false; } #else CodelyLogger.LogWarning("MacOSWebViewBridge: WebView is only supported on macOS"); return false; #endif } /// /// Create and initialize the WebView /// public bool Create(IntPtr guiViewHandle, string url, float x, float y, float w, float h) { #if UNITY_EDITOR_OSX try { if (_isInitialized) { CodelyLogger.LogWarning("MacOSWebViewBridge: WebView already initialized"); return false; } if (guiViewHandle == IntPtr.Zero) { CodelyLogger.LogError("MacOSWebViewBridge: Invalid GUIView handle"); return false; } _webViewHandle = WebView_Create(guiViewHandle, url, x, y, w, h); if (_webViewHandle != IntPtr.Zero) { _isInitialized = true; return true; } else { CodelyLogger.LogError("MacOSWebViewBridge: Failed to create WebView (returned null handle)"); return false; } } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Exception creating WebView: {ex.Message}\n{ex.StackTrace}"); return false; } #else CodelyLogger.LogWarning("MacOSWebViewBridge: WebView is only supported on macOS"); return false; #endif } /// /// Destroy the WebView /// public void Destroy() { #if UNITY_EDITOR_OSX // Uninstall drag gateway first UninstallDragGateway(); if (_webViewHandle != IntPtr.Zero) { try { WebView_Destroy(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error destroying WebView: {ex.Message}"); } finally { _webViewHandle = IntPtr.Zero; _isInitialized = false; } } #endif } /// /// Update WebView frame (position and size) /// public void UpdateFrame(float x, float y, float w, float h) { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_UpdateFrame(_webViewHandle, x, y, w, h); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error updating frame: {ex.Message}"); } } #endif } /// /// Update parent view and frame (when window/tab changes) /// public void UpdateParentAndFrame(IntPtr guiViewPtr, float x, float y, float w, float h) { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_UpdateParentAndFrame(_webViewHandle, guiViewPtr, x, y, w, h); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error updating parent and frame: {ex.Message}"); } } #endif } /// /// Load a URL /// public void LoadURL(string url) { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero && !string.IsNullOrEmpty(url)) { try { WebView_LoadURL(_webViewHandle, url); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error loading URL: {ex.Message}"); } } #endif } /// /// Navigate back /// public void GoBack() { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_GoBack(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error going back: {ex.Message}"); } } #endif } /// /// Navigate forward /// public void GoForward() { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_GoForward(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error going forward: {ex.Message}"); } } #endif } /// /// Reload the page /// public void Reload() { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_Reload(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error reloading: {ex.Message}"); } } #endif } /// /// Execute JavaScript in the WebView /// public void ExecuteJavaScript(string script) { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero && !string.IsNullOrEmpty(script)) { try { WebView_ExecuteJavaScript(_webViewHandle, script); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error executing JavaScript: {ex.Message}"); } } #endif } /// /// Set WebView visibility (hide/show) /// public void SetHidden(bool hidden) { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_SetHidden(_webViewHandle, hidden); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error setting visibility: {ex.Message}"); } } #endif } /// /// Show Web Inspector for debugging /// public void ShowInspector() { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_ShowInspector(_webViewHandle); CodelyLogger.Log("MacOSWebViewBridge: Opening Web Inspector..."); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error showing inspector: {ex.Message}"); } } else { CodelyLogger.LogWarning("MacOSWebViewBridge: Cannot show inspector - WebView not initialized"); } #endif } /// /// Move keyboard focus to the embedded WKWebView. /// public void Focus() { #if UNITY_EDITOR_OSX if (_webViewHandle != IntPtr.Zero) { try { WebView_Focus(_webViewHandle); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error focusing WebView: {ex.Message}"); } } #endif } /// /// Install Unity Drag Gateway to enable GameObject drag and drop /// Must be called after WebView is created and guiRenderView is available /// public bool InstallDragGateway(IntPtr guiRenderViewPtr) { #if UNITY_EDITOR_OSX if (_webViewHandle == IntPtr.Zero) { CodelyLogger.LogError("MacOSWebViewBridge: Cannot install drag gateway - WebView not initialized"); return false; } if (guiRenderViewPtr == IntPtr.Zero) { CodelyLogger.LogError("MacOSWebViewBridge: Cannot install drag gateway - GUIRenderView is null"); return false; } if (_dragGatewayHandle != IntPtr.Zero) { CodelyLogger.LogWarning("MacOSWebViewBridge: Drag gateway already installed"); return true; } try { _dragGatewayHandle = InstallUnityDragGateway(_webViewHandle, guiRenderViewPtr); if (_dragGatewayHandle != IntPtr.Zero) { CodelyLogger.Log("MacOSWebViewBridge: Drag gateway installed successfully"); return true; } else { CodelyLogger.LogError("MacOSWebViewBridge: Failed to install drag gateway"); return false; } } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Exception installing drag gateway: {ex.Message}\n{ex.StackTrace}"); return false; } #else return false; #endif } /// /// Uninstall Unity Drag Gateway /// public void UninstallDragGateway() { #if UNITY_EDITOR_OSX if (_dragGatewayHandle != IntPtr.Zero) { try { UninstallUnityDragGateway(_dragGatewayHandle); CodelyLogger.Log("MacOSWebViewBridge: Drag gateway uninstalled"); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error uninstalling drag gateway: {ex.Message}"); } finally { _dragGatewayHandle = IntPtr.Zero; } } #endif } /// /// Update drag gateway frame to match WebView frame /// Should be called whenever WebView frame changes /// public void UpdateDragGatewayFrame(float x, float y, float w, float h) { #if UNITY_EDITOR_OSX if (_dragGatewayHandle != IntPtr.Zero) { try { UpdateDragGatewayFrame(_dragGatewayHandle, x, y, w, h); } catch (Exception ex) { CodelyLogger.LogError($"MacOSWebViewBridge: Error updating drag gateway frame: {ex.Message}"); } } #endif } } } #endif