using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
namespace NetWebView2Lib
{
// --- 1. EVENTS INTERFACE (What C# sends to AutoIt) ---
[Guid("B2C3D4E5-F6A7-4B6C-9D0E-1F2A3B4C5D6E")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
///
/// Events sent from C# to AutoIt.
///
public interface IWebViewEvents
{
[DispId(1)]
void OnMessageReceived(string message);
}
// --- 2. ACTIONS INTERFACE (What AutoIt can call in C#) ---
[Guid("CCB12345-6789-4ABC-DEF0-1234567890AB")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IWebViewActions
{
[DispId(101)] void Initialize(object parentHandle, object initialUrlOrNull = null, int x = 0, int y = 0, int width = 0, int height = 0);
[DispId(102)] void Navigate(string url);
[DispId(103)] void NavigateToString(string htmlContent);
[DispId(104)] void ExecuteScript(string script);
[DispId(105)] void Resize(int width, int height);
[DispId(106)] void Cleanup();
[DispId(107)] IBridgeActions GetBridge();
[DispId(108)] void ExportToPdf(string filePath);
[DispId(109)] bool IsReady();
[DispId(110)] void SetContextMenuEnabled(bool enabled);
[DispId(111)] void LockWebView();
[DispId(112)] void DisableBrowserFeatures();
[DispId(113)] void GoBack();
[DispId(114)] void ResetZoom();
[DispId(115)] void InjectCss(string cssCode);
[DispId(116)] void ClearInjectedCss();
[DispId(117)] void ToggleAuditHighlights(bool enable);
[DispId(118)] void SetAdBlock(bool active);
[DispId(119)] void AddBlockRule(string domain);
[DispId(120)] void ClearBlockRules();
[DispId(121)] void GoForward();
[DispId(122)] void GetHtmlSource();
[DispId(123)] void GetSelectedText();
[DispId(124)] void SetZoom(double factor);
[DispId(125)] bool ParseJsonToInternal(string json);
[DispId(126)] string GetInternalJsonValue(string path);
[DispId(127)] void ClearBrowserData();
[DispId(128)] void Reload();
[DispId(129)] void Stop();
[DispId(130)] void ShowPrintUI();
[DispId(131)] void SetMuted(bool muted);
[DispId(132)] bool IsMuted();
}
// --- 3. THE MANAGER CLASS ---
[Guid("A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D")]
[ComSourceInterfaces(typeof(IWebViewEvents))]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[ProgId("NetWebView2.Manager")]
public class WebViewManager : IWebViewActions
{
// --- PRIVATE FIELDS ---
private readonly WebView2 _webView;
private readonly WebViewBridge _bridge;
private readonly JsonParser _internalParser = new JsonParser();
private bool _isAdBlockActive = false;
private List _blockList = new List();
private const string StyleId = "autoit-injected-style";
private bool _contextMenuEnabled = true;
// --- EVENTS ---
public delegate void OnMessageReceivedDelegate(string message);
public event OnMessageReceivedDelegate OnMessageReceived;
// --- NATIVE METHODS ---
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr hWnd, out Rect lpRect);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[StructLayout(LayoutKind.Sequential)]
public struct Rect { public int Left, Top, Right, Bottom; }
// --- CONSTRUCTOR ---
///
/// Initializes a new instance of the WebViewManager class.
///
public WebViewManager()
{
_webView = new WebView2();
_bridge = new WebViewBridge();
}
// --- PROPERTIES ---
/// Get the Bridge object for AutoIt interaction
public IBridgeActions GetBridge()
{
return _bridge;
}
/// Check if WebView2 is initialized and ready
public bool IsReady() => _webView?.CoreWebView2 != null;
// --- INITIALIZATION ---
///
/// Initializes the WebView2 control within the specified parent window handle.
///
public async void Initialize(object parentHandle, object initialUrlOrNull = null, int x = 0, int y = 0, int width = 0, int height = 0)
{
try
{
if (parentHandle == null) throw new ArgumentException("Parent handle is null.");
// Create a local copy of the handle to protect the original AutoIt variable
long rawHandleValue = Convert.ToInt64(parentHandle);
IntPtr localParentPtr = new IntPtr(rawHandleValue);
if (localParentPtr == IntPtr.Zero) throw new ArgumentException("Invalid Parent Handle (IntPtr.Zero).");
if (width == 0 || height == 0)
{
if (GetClientRect(localParentPtr, out Rect rect))
{
width = (width == 0) ? Math.Max(0, rect.Right - rect.Left) : width;
height = (height == 0) ? Math.Max(0, rect.Bottom - rect.Top) : height;
}
}
InvokeOnUiThread(() =>
{
_webView.Location = new Point(x, y);
_webView.Size = new Size(width, height);
SetParent(_webView.Handle, localParentPtr);
_webView.Visible = false;
});
string userDataFolder = Path.Combine(Path.GetTempPath(), "WebView2_AutoIt_Cache");
var env = await CoreWebView2Environment.CreateAsync(null, userDataFolder);
await _webView.EnsureCoreWebView2Async(env);
//---load extension fail--------------------------------------------------
var options = new CoreWebView2EnvironmentOptions(userDataFolder);
options.AreBrowserExtensionsEnabled = true;
var path = @"D:\WebView2AutoIt_v0.1\uBOLite_2025.1224.1544.edge\";
await _webView.CoreWebView2.Profile.AddBrowserExtensionAsync(path);
//-----------------------------------------------------------------------
await Task.Delay(150); // Stabilization delay
ConfigureSettings();
RegisterEvents();
InvokeOnUiThread(() => _webView.Visible = true);
// Notify AutoIt that WebView2 is ready
OnMessageReceived?.Invoke("INIT_READY");
if (initialUrlOrNull is string url && !string.IsNullOrEmpty(url))
{
Navigate(url);
}
}
catch (Exception ex)
{
Debug.WriteLine($"[WebView2] Init Error: {ex.Message}");
OnMessageReceived?.Invoke($"ERROR|INIT_FAILED|{ex.Message}");
}
}
// --- CONFIGURATION ---
/// Configure WebView2 settings
private void ConfigureSettings()
{
var settings = _webView.CoreWebView2.Settings;
settings.IsWebMessageEnabled = true; // Enable Web Messages
settings.AreDevToolsEnabled = true; // Enable DevTools by default
settings.AreDefaultContextMenusEnabled = false; // Disable default context menus
_webView.DefaultBackgroundColor = Color.Transparent;
}
/// Disable certain browser features for a controlled environment
public void DisableBrowserFeatures()
{
InvokeOnUiThread(() => {
if (_webView?.CoreWebView2 != null)
{
var settings = _webView.CoreWebView2.Settings;
settings.AreDevToolsEnabled = false; // Disable DevTools
settings.IsStatusBarEnabled = false; // Disable Status Bar
settings.IsZoomControlEnabled = false; // Disable Zoom Control
}
});
}
// --- EVENT REGISTRATION ---
/// Register event handlers for WebView2 events
private void RegisterEvents()
{
// Context Menu Event
_webView.CoreWebView2.ContextMenuRequested += (s, e) =>
{
if (!_contextMenuEnabled)
{
e.Handled = true;
return;
}
string contextType = e.ContextMenuTarget.Kind.ToString();
string linkUri = e.ContextMenuTarget.HasLinkUri ? e.ContextMenuTarget.LinkUri : "";
string sourceUri = e.ContextMenuTarget.HasSourceUri ? e.ContextMenuTarget.SourceUri : "";
OnMessageReceived?.Invoke($"CONTEXT_MENU|{e.Location.X}|{e.Location.Y}|{contextType}|{linkUri}|{sourceUri}");
};
// Ad Blocking
_webView.CoreWebView2.AddWebResourceRequestedFilter("*", CoreWebView2WebResourceContext.All);
_webView.CoreWebView2.WebResourceRequested += (s, e) =>
{
if (!_isAdBlockActive) return;
string uri = e.Request.Uri.ToLower();
foreach (var domain in _blockList)
{
if (uri.Contains(domain))
{
e.Response = _webView.CoreWebView2.Environment.CreateWebResourceResponse(null, 403, "Forbidden", "");
OnMessageReceived?.Invoke($"BLOCKED_AD|{uri}");
return;
}
}
};
// Navigation and State Events
_webView.CoreWebView2.DownloadStarting += (s, e) =>
OnMessageReceived?.Invoke($"DOWNLOAD_STARTING|{e.DownloadOperation.ResultFilePath}|{e.DownloadOperation.Uri}");
_webView.CoreWebView2.NewWindowRequested += (s, e) => { e.Handled = true; _webView.CoreWebView2.Navigate(e.Uri); };
_webView.NavigationStarting += (s, e) => OnMessageReceived?.Invoke("NAV_STARTING");
_webView.NavigationCompleted += (s, e) => {
if (e.IsSuccess)
{
OnMessageReceived?.Invoke("NAV_COMPLETED");
OnMessageReceived?.Invoke("TITLE_CHANGED|" + _webView.CoreWebView2.DocumentTitle);
}
else OnMessageReceived?.Invoke("NAV_ERROR|" + e.WebErrorStatus);
};
_webView.CoreWebView2.SourceChanged += (s, e) => OnMessageReceived?.Invoke("URL_CHANGED|" + _webView.Source);
// --- Η ΔΙΟΡΘΩΣΗ ΕΔΩ ---
// Αντί να στέλνουμε το WebMessage στο OnMessageReceived (Manager),
// το στέλνουμε στο Bridge για να το πιάσει το Bridge_OnMessageReceived στο AutoIt.
_webView.CoreWebView2.WebMessageReceived += (s, e) =>
{
string message = e.TryGetWebMessageAsString();
_bridge.RaiseMessage(message); // Στέλνει το μήνυμα στο σωστό κανάλι
};
_webView.CoreWebView2.AddHostObjectToScript("autoit", _bridge);
}
// --- PUBLIC API METHODS ---
/// Clear browser data (cookies, cache, history, etc.)
public async void ClearBrowserData()
{
await _webView.EnsureCoreWebView2Async();
// Clears cookies, history, cache, etc.
await _webView.CoreWebView2.Profile.ClearBrowsingDataAsync();
OnMessageReceived?.Invoke("DATA_CLEARED");
}
/// Lock down the WebView by disabling certain features
public void LockWebView()
{
InvokeOnUiThread(() => {
if (_webView?.CoreWebView2 != null)
{
var s = _webView.CoreWebView2.Settings;
s.AreDefaultContextMenusEnabled = false; // Disable context menus
s.AreDevToolsEnabled = false; // Disable DevTools
s.IsZoomControlEnabled = false; // Disable Zoom Control
s.IsBuiltInErrorPageEnabled = false; // Disable built-in error pages
}
});
}
/// Stops any ongoing navigation or loading
public void Stop()
{
_webView?.CoreWebView2.Stop();
}
/// Shows the print UI dialog
public void ShowPrintUI()
{
_webView?.CoreWebView2.ShowPrintUI();
}
/// Sets the mute status for audio
public void SetMuted(bool muted)
{
if (_webView?.CoreWebView2 != null)
_webView.CoreWebView2.IsMuted = muted;
}
/// Gets the current mute status
public bool IsMuted()
{
return _webView?.CoreWebView2?.IsMuted ?? false;
}
/// Reload the current page
public void Reload()
{
// Check if CoreWebView2 is initialized to avoid null reference exceptions
if (_webView != null && _webView.CoreWebView2 != null)
{
_webView.CoreWebView2.Reload();
}
}
/// Navigate back in history
public void GoBack() => InvokeOnUiThread(() => {
if (_webView?.CoreWebView2 != null && _webView.CoreWebView2.CanGoBack)
_webView.CoreWebView2.GoBack();
});
/// Navigate forward in history
public void GoForward() => InvokeOnUiThread(() => {
if (_webView?.CoreWebView2 != null && _webView.CoreWebView2.CanGoForward)
_webView.CoreWebView2.GoForward();
});
/// Reset zoom to default (100%)
public void ResetZoom() => SetZoom(1.0);
/// Clear all ad block rules
public void ClearBlockRules() => _blockList.Clear();
/// Enable or disable the default context menu
public void SetContextMenuEnabled(bool enabled)
{
_contextMenuEnabled = enabled; // Store preference
InvokeOnUiThread(() => {
if (_webView?.CoreWebView2 != null)
_webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled = enabled;
});
}
/// Navigate to a specified URL
public void Navigate(string url) => InvokeOnUiThread(() => _webView.CoreWebView2?.Navigate(url));
/// Navigate to a string containing HTML content
public void NavigateToString(string htmlContent)
{
_webView.Invoke(new Action(async () => {
int attempts = 0;
while (_webView.CoreWebView2 == null && attempts < 20) { await Task.Delay(50); attempts++; }
_webView.CoreWebView2?.NavigateToString(htmlContent);
}));
}
/// Execute arbitrary JavaScript code
public void ExecuteScript(string script)
{
if (_webView?.CoreWebView2 != null)
_webView.Invoke(new Action(() => _webView.CoreWebView2.ExecuteScriptAsync(script)));
}
/// Inject CSS code into the current page
public void InjectCss(string cssCode)
{
string js = $"(function() {{ let style = document.getElementById('{StyleId}'); if (!style) {{ style = document.createElement('style'); style.id = '{StyleId}'; document.head.appendChild(style); }} style.innerHTML = `{cssCode}`; }})();";
ExecuteScript(js);
}
/// Remove previously injected CSS
public void ClearInjectedCss() => ExecuteScript($"(function() {{ let style = document.getElementById('{StyleId}'); if (style) style.remove(); }})();");
/// Toggle audit highlights on/off
public void ToggleAuditHighlights(bool enable)
{
if (enable) InjectCss("img, h1, h2, h3, table, a { outline: 3px solid #FF6A00 !important; outline-offset: -3px !important; }");
else ClearInjectedCss();
}
/// Retrieve the full HTML source of the current page
public async void GetHtmlSource()
{
if (_webView?.CoreWebView2 == null) return;
string html = await _webView.CoreWebView2.ExecuteScriptAsync("document.documentElement.outerHTML");
OnMessageReceived?.Invoke("HTML_SOURCE|" + CleanJsString(html));
}
/// Retrieve the currently selected text on the page
public async void GetSelectedText()
{
if (_webView?.CoreWebView2 == null) return;
string selectedText = await _webView.CoreWebView2.ExecuteScriptAsync("window.getSelection().toString()");
OnMessageReceived?.Invoke("SELECTED_TEXT|" + CleanJsString(selectedText));
}
/// Clean up JavaScript string results
private string CleanJsString(string input)
{
string decoded = System.Text.RegularExpressions.Regex.Unescape(input);
if (decoded.StartsWith("\"") && decoded.EndsWith("\"") && decoded.Length >= 2)
decoded = decoded.Substring(1, decoded.Length - 2);
return decoded;
}
/// Resize the WebView control
public void Resize(int w, int h) => InvokeOnUiThread(() => _webView.Size = new Size(w, h));
/// Clean up resources
public void Cleanup() => _webView?.Dispose();
/// Set the zoom factor
public void SetZoom(double factor) => InvokeOnUiThread(() => _webView.ZoomFactor = factor);
/// Export the current page to a PDF file
public void ExportToPdf(string filePath)
{
InvokeOnUiThread(async () => {
try
{
if (_webView?.CoreWebView2 != null)
{
await _webView.CoreWebView2.PrintToPdfAsync(filePath, null);
OnMessageReceived?.Invoke("PDF_SUCCESS|" + filePath);
}
}
catch (Exception ex) { OnMessageReceived?.Invoke("PDF_ERROR|" + ex.Message); }
});
}
/// Ad Block Methods
public void SetAdBlock(bool active) => _isAdBlockActive = active;
/// Add a domain to the block list
public void AddBlockRule(string domain) { if (!string.IsNullOrEmpty(domain)) _blockList.Add(domain.ToLower()); }
/// Parse JSON into the internal parser
public bool ParseJsonToInternal(string json) => _internalParser.Parse(json);
/// Get a value from the internal JSON parser
public string GetInternalJsonValue(string path) => _internalParser.GetTokenValue(path);
// --- HELPER METHODS ---
/// Invoke actions on the UI thread
private void InvokeOnUiThread(Action action)
{
if (_webView == null || _webView.IsDisposed) return;
if (_webView.InvokeRequired) _webView.Invoke(action);
else action();
}
}
}