mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-06-09 10:06:34 +02:00
83c2a1be11
* [Dependency] source generator No more reflection, no more codegen at runtime Also various changes to Roslyn helpers to make this easier to write. Requires all types with dependencies to be partial and not have readonly dependency fields. An analyzer enforces this at warning level, the previous injection strategies have remained in the code *for now* as a fallback. No fallback is available for [field: Dependency] properties, due to a Roslyn bug. Code Fixes exist. We love Roslyn * Apply dependencies generator changes to all code * Release notes * Preprocessor got hands * Handle nullable dependencies These are bad but gotta deal with it. * Apply suggestions from code review Co-authored-by: Moony <moony@hellomouse.net> * Fine, let's not use collection expressions --------- Co-authored-by: Moony <moony@hellomouse.net>
228 lines
8.5 KiB
C#
228 lines
8.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using Robust.Client.Console;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.ContentPack;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Localization;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
using Xilium.CefGlue;
|
|
|
|
namespace Robust.Client.WebView.Cef
|
|
{
|
|
internal sealed partial class WebViewManagerCef : IWebViewManagerImpl
|
|
{
|
|
private static readonly string BasePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location!)!;
|
|
|
|
private CefApp _app = default!;
|
|
|
|
[Dependency] private IDependencyCollection _dependencyCollection = default!;
|
|
[Dependency] private IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private IGameControllerInternal _gameController = default!;
|
|
[Dependency] private IResourceManagerInternal _resourceManager = default!;
|
|
[Dependency] private IClientConsoleHost _consoleHost = default!;
|
|
[Dependency] private IConfigurationManager _cfg = default!;
|
|
[Dependency] private ILogManager _logManager = default!;
|
|
[Dependency] private ILocalizationManager _localization = default!;
|
|
|
|
private ISawmill _sawmill = default!;
|
|
|
|
public void Initialize()
|
|
{
|
|
_sawmill = _logManager.GetSawmill("web.cef");
|
|
|
|
_consoleHost.RegisterCommand(
|
|
"flushcookies",
|
|
_localization.GetString("cmd-flushcookies-desc"),
|
|
_localization.GetString("cmd-flushcookies-help"),
|
|
(_, _, _) => CefCookieManager.GetGlobal(null).FlushStore(null));
|
|
|
|
#if !MACOS
|
|
string subProcessName;
|
|
if (OperatingSystem.IsWindows())
|
|
subProcessName = "Robust.Client.WebView.exe";
|
|
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
|
subProcessName = "Robust.Client.WebView";
|
|
else
|
|
throw new NotSupportedException("Unsupported platform for CEF!");
|
|
|
|
var subProcessPath = Path.Combine(BasePath, subProcessName);
|
|
var cefResourcesPath = LocateCefResources();
|
|
_sawmill.Debug($"Subprocess path: {subProcessPath}, resources: {cefResourcesPath}");
|
|
|
|
// System.Console.WriteLine(AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES"));
|
|
|
|
if (cefResourcesPath == null)
|
|
throw new InvalidOperationException("Unable to locate cef_resources directory!");
|
|
#endif
|
|
|
|
var remoteDebugPort = _cfg.GetCVar(WCVars.WebRemoteDebugPort);
|
|
|
|
var cachePath = FindAndLockCacheDirectory();
|
|
|
|
#if MACOS
|
|
NativeLibrary.SetDllImportResolver(typeof(CefSettings).Assembly,
|
|
(name, assembly, path) =>
|
|
{
|
|
if (name == "libcef")
|
|
{
|
|
var libPath = PathHelpers.ExecutableRelativeFile("../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework");
|
|
return NativeLibrary.Load(libPath, assembly, path);
|
|
}
|
|
|
|
return 0;
|
|
});
|
|
|
|
// Needed to implement CefAppProtocol on our NSApplication.
|
|
NativeLibrary.Load("robust_native_webview", typeof(WebViewManagerCef).Assembly, null);
|
|
#endif
|
|
|
|
var settings = new CefSettings()
|
|
{
|
|
WindowlessRenderingEnabled = true, // So we can render to our UI controls.
|
|
ExternalMessagePump = true,
|
|
NoSandbox = true, // Not disabling the sandbox crashes CEF.
|
|
#if !MACOS
|
|
BrowserSubprocessPath = subProcessPath,
|
|
LocalesDirPath = Path.Combine(cefResourcesPath, "locales"),
|
|
ResourcesDirPath = cefResourcesPath,
|
|
#endif
|
|
RemoteDebuggingPort = remoteDebugPort,
|
|
CookieableSchemesList = "usr,res",
|
|
CachePath = cachePath,
|
|
};
|
|
|
|
var userAgentOverride = _cfg.GetCVar(WCVars.WebUserAgentOverride);
|
|
if (!string.IsNullOrEmpty(userAgentOverride))
|
|
{
|
|
settings.UserAgent = userAgentOverride;
|
|
}
|
|
|
|
_sawmill.Info($"CEF Version: {CefRuntime.ChromeVersion}");
|
|
|
|
_app = new RobustCefApp(_sawmill);
|
|
|
|
var process = Process.GetCurrentProcess();
|
|
Environment.SetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_ID", process.Id.ToString());
|
|
Environment.SetEnvironmentVariable("ROBUST_CEF_BROWSER_PROCESS_MODULE", process.MainModule?.FileName ?? "");
|
|
|
|
// So these arguments look like nonsense, but it turns out CEF is just *like that*.
|
|
// The first argument is literally nonsense, but it needs to be there as otherwise the second argument doesn't apply
|
|
// The second argument turns off CEF's bullshit error handling, which breaks dotnet's error handling.
|
|
CefRuntime.Initialize(new CefMainArgs(new string[]{"binary","--disable-in-process-stack-traces"}), settings, _app, IntPtr.Zero);
|
|
|
|
if (_cfg.GetCVar(WCVars.WebResProtocol))
|
|
{
|
|
var handler = new ResourceSchemeFactoryHandler(
|
|
this,
|
|
_resourceManager,
|
|
_logManager.GetSawmill("web.res"));
|
|
|
|
CefRuntime.RegisterSchemeHandlerFactory("res", "", handler);
|
|
}
|
|
}
|
|
|
|
private static string? LocateCefResources()
|
|
{
|
|
if (ProbeDir(BasePath, out var path))
|
|
return path;
|
|
|
|
foreach (var searchDir in NativeDllSearchDirectories())
|
|
{
|
|
if (ProbeDir(searchDir, out path))
|
|
return path;
|
|
}
|
|
|
|
return null;
|
|
|
|
static bool ProbeDir(string dir, out string path)
|
|
{
|
|
path = Path.Combine(dir, "cef_resources");
|
|
return Directory.Exists(path);
|
|
}
|
|
}
|
|
|
|
internal static string[] NativeDllSearchDirectories()
|
|
{
|
|
var sepChar = OperatingSystem.IsWindows() ? ';' : ':';
|
|
|
|
var searchDirectories = ((string)AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES")!)
|
|
.Split(sepChar, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
return searchDirectories;
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
// Calling this makes CEF do its work, without using its own update loop.
|
|
CefRuntime.DoMessageLoopWork();
|
|
}
|
|
|
|
public void Shutdown()
|
|
{
|
|
foreach (var control in _activeControls.ToArray())
|
|
{
|
|
control.CloseBrowser();
|
|
}
|
|
|
|
foreach (var window in _browserWindows.ToArray())
|
|
{
|
|
window.Dispose();
|
|
}
|
|
|
|
CefRuntime.Shutdown();
|
|
}
|
|
|
|
private sealed class ResourceSchemeFactoryHandler : CefSchemeHandlerFactory
|
|
{
|
|
private readonly WebViewManagerCef _parent;
|
|
private readonly IResourceManager _resourceManager;
|
|
private readonly ISawmill _sawmill;
|
|
|
|
public ResourceSchemeFactoryHandler(
|
|
WebViewManagerCef parent,
|
|
IResourceManager resourceManager,
|
|
ISawmill sawmill)
|
|
{
|
|
_parent = parent;
|
|
_resourceManager = resourceManager;
|
|
_sawmill = sawmill;
|
|
}
|
|
|
|
protected override CefResourceHandler Create(
|
|
CefBrowser browser,
|
|
CefFrame frame,
|
|
string schemeName,
|
|
CefRequest request)
|
|
{
|
|
var uri = new Uri(request.Url);
|
|
|
|
_sawmill.Debug($"HANDLING: {request.Url}");
|
|
|
|
var resPath = new ResPath(uri.AbsolutePath);
|
|
if (_resourceManager.TryContentFileRead(resPath, out var stream))
|
|
{
|
|
if (!_parent.TryGetResourceMimeType(resPath.Extension, out var mime))
|
|
mime = "application/octet-stream";
|
|
|
|
return new RequestResultStream(stream, mime, HttpStatusCode.OK).MakeHandler();
|
|
}
|
|
|
|
var notFoundStream = new MemoryStream();
|
|
notFoundStream.Write(Encoding.UTF8.GetBytes("Not found"));
|
|
notFoundStream.Position = 0;
|
|
|
|
return new RequestResultStream(notFoundStream, "text/plain", HttpStatusCode.NotFound).MakeHandler();
|
|
}
|
|
}
|
|
}
|
|
}
|