Implement res:// protocol for WebView, MIME type handling.

This commit is contained in:
Pieter-Jan Briers
2022-06-10 17:09:46 +02:00
parent 05bb361eb2
commit 9591d16aed
6 changed files with 210 additions and 2 deletions

View File

@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Utility;
namespace Robust.Client.WebView.Cef;
internal sealed partial class WebViewManagerCef
{
// Loosely based on:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
private readonly Dictionary<string, string> _resourceMimeTypes = new()
{
{ "aac", "audio/aac" },
{ "avif", "image/avif" },
{ "avi", "video/x-msvideo" },
{ "bmp", "image/bmp" },
{ "css", "text/css" },
{ "gif", "image/gif" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "ico", "image/vnd.microsoft.icon" },
{ "jpeg", "image/jpeg" },
{ "jpg", "image/jpeg" },
{ "js", "text/javascript" },
{ "json", "application/json" },
{ "jsonld", "application/ld+json" },
{ "midi", "audio/midi" },
{ "mid", "audio/midi" },
{ "mjs", "text/javascript" },
{ "mp3", "audio/mpeg" },
{ "mp4", "video/mp4" },
{ "mpeg", "video/mpeg" },
{ "oga", "audio/ogg" },
{ "ogg", "audio/ogg" },
{ "ogv", "video/ogg" },
{ "ogx", "application/ogg" },
{ "opus", "audio/opus" },
{ "otf", "font/otf" },
{ "png", "image/png" },
{ "pdf", "application/pdf" },
{ "svg", "image/svg+xml" },
{ "tiff", "image/tiff" },
{ "tif", "image/tiff" },
{ "ts", "video/mp2t" },
{ "ttf", "font/ttf" },
{ "txt", "text/plain" },
{ "wav", "audio/wav" },
{ "weba", "audio/webm" },
{ "webm", "video/webm" },
{ "webp", "image/webp" },
{ "woff", "font/woff" },
{ "woff2", "font/woff2" },
{ "xhtml", "application/xhtml+xml" },
{ "xml", "application/xml" },
{ "zip", "application/zip" },
};
public void SetResourceMimeType(string extension, string mimeType)
{
DebugTools.Assert(!extension.StartsWith("."), "SetResourceMimeType extension must not include starting dot.");
lock (_resourceMimeTypes)
{
_resourceMimeTypes[extension] = mimeType;
}
}
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
{
lock (_resourceMimeTypes)
{
return _resourceMimeTypes.TryGetValue(extension, out mimeType);
}
}
}

View File

@@ -2,7 +2,9 @@ using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using Robust.Client.Console;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
@@ -23,6 +25,7 @@ namespace Robust.Client.WebView.Cef
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public void Initialize()
{
@@ -76,6 +79,12 @@ namespace Robust.Client.WebView.Cef
// TODO CEF: After this point, debugging breaks. No, literally. My client crashes but ONLY with the debugger.
// I have tried using the DEBUG and RELEASE versions of libcef.so, stripped or non-stripped...
// And nothing seemed to work. Odd.
if (_cfg.GetCVar(WCVars.WebResProtocol))
{
var handler = new ResourceSchemeFactoryHandler(this, _resourceManager, Logger.GetSawmill("web.res"));
CefRuntime.RegisterSchemeHandlerFactory("res", "", handler);
}
}
private static string? LocateCefResources()
@@ -119,5 +128,48 @@ namespace Robust.Client.WebView.Cef
{
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 resourcePath = new ResourcePath(uri.AbsolutePath);
if (_resourceManager.TryContentFileRead(resourcePath, out var stream))
{
if (!_parent.TryGetResourceMimeType(resourcePath.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();
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
@@ -11,6 +12,17 @@ namespace Robust.Client.WebView.Headless
return new WebViewWindowDummy();
}
public void SetResourceMimeType(string extension, string mimeType)
{
// Nop
}
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
{
mimeType = null;
return false;
}
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
return new WebViewControlImplDummy();

View File

@@ -1,7 +1,40 @@
namespace Robust.Client.WebView
using System.Diagnostics.CodeAnalysis;
namespace Robust.Client.WebView
{
public interface IWebViewManager
{
IWebViewWindow CreateBrowserWindow(BrowserWindowCreateParameters createParams);
/// <summary>
/// Overrides file extension -> mime type mappings for the <c>res://</c> protocol.
/// </summary>
/// <remarks>
/// <para>
/// The built-in <c>res://</c> protocol needs to guess MIME types to report to CEF when resolving files.
/// A limited set of extensions have pre-set MIME types in the engine.
/// This method allows you to replace or add entries if need be.
/// </para>
/// <para>
/// This method is thread safe.
/// </para>
/// </remarks>
/// <param name="extension">
/// The extension to specify the MIME type for.
/// The argument must not include the starting "." of the file extension.
/// </param>
/// <param name="mimeType">The mime type for this file extension.</param>
/// <seealso cref="TryGetResourceMimeType"/>
void SetResourceMimeType(string extension, string mimeType);
/// <summary>
/// Tries to resolve an entry from the <see cref="SetResourceMimeType"/> list.
/// </summary>
/// <remarks>
/// <para>
/// This method is thread safe.
/// </para>
/// </remarks>
bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType);
}
}

View File

@@ -0,0 +1,17 @@
using Robust.Shared.Configuration;
namespace Robust.Client.WebView;
// ReSharper disable once InconsistentNaming
/// <summary>
/// CVars for <c>Robust.Client.WebView</c>
/// </summary>
[CVarDefs]
public static class WCVars
{
/// <summary>
/// Enable the <c>res://</c> protocol inside WebView browsers, allowing access to the Robust resources.
/// </summary>
public static readonly CVarDef<bool> WebResProtocol =
CVarDef.Create("web.res_protocol", true, CVar.CLIENTONLY);
}

View File

@@ -1,7 +1,9 @@
using Robust.Client.WebView;
using System.Diagnostics.CodeAnalysis;
using Robust.Client.WebView;
using Robust.Client.WebView.Cef;
using Robust.Client.WebView.Headless;
using Robust.Client.WebViewHook;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
@@ -17,6 +19,9 @@ namespace Robust.Client.WebView
{
DebugTools.Assert(_impl == null, "WebViewManager has already been initialized!");
var cfg = IoCManager.Resolve<IConfigurationManagerInternal>();
cfg.LoadCVarsFromAssembly(typeof(WebViewManager).Assembly);
IoCManager.RegisterInstance<IWebViewManager>(this);
IoCManager.RegisterInstance<IWebViewManagerInternal>(this);
@@ -49,6 +54,20 @@ namespace Robust.Client.WebView
return _impl!.CreateBrowserWindow(createParams);
}
public void SetResourceMimeType(string extension, string mimeType)
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
_impl!.SetResourceMimeType(extension, mimeType);
}
public bool TryGetResourceMimeType(string extension, [NotNullWhen(true)] out string? mimeType)
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");
return _impl!.TryGetResourceMimeType(extension, out mimeType);
}
public IWebViewControlImpl MakeControlImpl(WebViewControl owner)
{
DebugTools.Assert(_impl != null, "WebViewManager has not yet been initialized!");