diff --git a/src/Web/Avalonia.Blazor/AssetLoader.cs b/src/Web/Avalonia.Blazor/AssetLoader.cs deleted file mode 100644 index 724b51dfd0..0000000000 --- a/src/Web/Avalonia.Blazor/AssetLoader.cs +++ /dev/null @@ -1,398 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using Avalonia.Platform; -using Avalonia.Utilities; - -namespace Avalonia.Blazor -{ - class AssetLoader : IAssetLoader - { - private const string AvaloniaResourceName = "!AvaloniaResources"; - private static readonly Dictionary AssemblyNameCache - = new Dictionary(); - - private AssemblyDescriptor _defaultResmAssembly; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The default assembly from which to load resm: assets for which no assembly is specified. - /// - public AssetLoader(Assembly assembly = null) - { - if (assembly == null) - assembly = Assembly.GetEntryAssembly(); - if (assembly != null) - _defaultResmAssembly = new AssemblyDescriptor(assembly); - } - - /// - /// Sets the default assembly from which to load assets for which no assembly is specified. - /// - /// The default assembly. - public void SetDefaultAssembly(Assembly assembly) - { - _defaultResmAssembly = new AssemblyDescriptor(assembly); - } - - /// - /// Checks if an asset with the specified URI exists. - /// - /// The URI. - /// - /// A base URI to use if is relative. - /// - /// True if the asset could be found; otherwise false. - public bool Exists(Uri uri, Uri baseUri = null) - { - return GetAsset(uri, baseUri) != null; - } - - /// - /// Opens the asset with the requested URI. - /// - /// The URI. - /// - /// A base URI to use if is relative. - /// - /// A stream containing the asset contents. - /// - /// The asset could not be found. - /// - public Stream Open(Uri uri, Uri baseUri = null) => OpenAndGetAssembly(uri, baseUri).Item1; - - /// - /// Opens the asset with the requested URI and returns the asset stream and the - /// assembly containing the asset. - /// - /// The URI. - /// - /// A base URI to use if is relative. - /// - /// - /// The stream containing the resource contents together with the assembly. - /// - /// - /// The asset could not be found. - /// - public (Stream stream, Assembly assembly) OpenAndGetAssembly(Uri uri, Uri baseUri = null) - { - var asset = GetAsset(uri, baseUri); - - if (asset == null) - { - throw new FileNotFoundException($"The resource {uri} could not be found."); - } - - return (asset.GetStream(), asset.Assembly); - } - - public Assembly GetAssembly(Uri uri, Uri baseUri) - { - if (!uri.IsAbsoluteUri && baseUri != null) - uri = new Uri(baseUri, uri); - return GetAssembly(uri).Assembly; - } - - /// - /// Gets all assets of a folder and subfolders that match specified uri. - /// - /// The URI. - /// Base URI that is used if is relative. - /// All matching assets as a tuple of the absolute path to the asset and the assembly containing the asset - public IEnumerable GetAssets(Uri uri, Uri baseUri) - { - if (uri.IsAbsoluteUri && uri.Scheme == "resm") - { - var assembly = GetAssembly(uri); - - return assembly?.Resources.Where(x => x.Key.Contains(uri.AbsolutePath)) - .Select(x =>new Uri($"resm:{x.Key}?assembly={assembly.Name}")) ?? - Enumerable.Empty(); - } - - uri = EnsureAbsolute(uri, baseUri); - if (uri.Scheme == "avares") - { - var (asm, path) = GetResAsmAndPath(uri); - if (asm == null) - { - throw new ArgumentException( - "No default assembly, entry assembly or explicit assembly specified; " + - "don't know where to look up for the resource, try specifying assembly explicitly."); - } - - if (asm?.AvaloniaResources == null) - return Enumerable.Empty(); - path = path.TrimEnd('/') + '/'; - return asm.AvaloniaResources.Where(r => r.Key.StartsWith(path)) - .Select(x => new Uri($"avares://{asm.Name}{x.Key}")); - } - - return Enumerable.Empty(); - } - - private Uri EnsureAbsolute(Uri uri, Uri baseUri) - { - if (uri.IsAbsoluteUri) - return uri; - if(baseUri == null) - throw new ArgumentException($"Relative uri {uri} without base url"); - if (!baseUri.IsAbsoluteUri) - throw new ArgumentException($"Base uri {baseUri} is relative"); - if (baseUri.Scheme == "resm") - throw new ArgumentException( - $"Relative uris for 'resm' scheme aren't supported; {baseUri} uses resm"); - return new Uri(baseUri, uri); - } - - private IAssetDescriptor GetAsset(Uri uri, Uri baseUri) - { - if (uri.IsAbsoluteUri && uri.Scheme == "resm") - { - var asm = GetAssembly(uri) ?? GetAssembly(baseUri) ?? _defaultResmAssembly; - - if (asm == null) - { - throw new ArgumentException( - "No default assembly, entry assembly or explicit assembly specified; " + - "don't know where to look up for the resource, try specifying assembly explicitly."); - } - - IAssetDescriptor rv; - - var resourceKey = uri.AbsolutePath; - asm.Resources.TryGetValue(resourceKey, out rv); - return rv; - } - - uri = EnsureAbsolute(uri, baseUri); - - if (uri.Scheme == "avares") - { - var (asm, path) = GetResAsmAndPath(uri); - if (asm.AvaloniaResources == null) - return null; - asm.AvaloniaResources.TryGetValue(path, out var desc); - return desc; - } - - throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); - } - - private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) - { - var asm = GetAssembly(uri.Authority); - return (asm, uri.AbsolutePath); - } - - private AssemblyDescriptor GetAssembly(Uri uri) - { - if (uri != null) - { - if (!uri.IsAbsoluteUri) - return null; - if (uri.Scheme == "avares") - return GetResAsmAndPath(uri).asm; - - if (uri.Scheme == "resm") - { - var qs = ParseQueryString(uri); - string assemblyName; - - if (qs.TryGetValue("assembly", out assemblyName)) - { - return GetAssembly(assemblyName); - } - } - } - - return null; - } - - private AssemblyDescriptor GetAssembly(string name) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - - AssemblyDescriptor rv; - if (!AssemblyNameCache.TryGetValue(name, out rv)) - { - var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - var match = loadedAssemblies.FirstOrDefault(a => a.GetName().Name == name); - if (match != null) - { - AssemblyNameCache[name] = rv = new AssemblyDescriptor(match); - } - else - { - // iOS does not support loading assemblies dynamically! - // -#if __IOS__ - throw new InvalidOperationException( - $"Assembly {name} needs to be referenced and explicitly loaded before loading resources"); -#else - AssemblyNameCache[name] = rv = new AssemblyDescriptor(Assembly.Load(name)); -#endif - } - } - - return rv; - } - - private Dictionary ParseQueryString(Uri uri) - { - return uri.Query.TrimStart('?') - .Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries) - .Select(p => p.Split('=')) - .ToDictionary(p => p[0], p => p[1]); - } - - private interface IAssetDescriptor - { - Stream GetStream(); - Assembly Assembly { get; } - } - - private class AssemblyResourceDescriptor : IAssetDescriptor - { - private readonly Assembly _asm; - private readonly string _name; - - public AssemblyResourceDescriptor(Assembly asm, string name) - { - _asm = asm; - _name = name; - } - - public Stream GetStream() - { - return _asm.GetManifestResourceStream(_name); - } - - public Assembly Assembly => _asm; - } - - private class AvaloniaResourceDescriptor : IAssetDescriptor - { - private readonly int _offset; - private readonly int _length; - public Assembly Assembly { get; } - - public AvaloniaResourceDescriptor(Assembly asm, int offset, int length) - { - _offset = offset; - _length = length; - Assembly = asm; - } - - public Stream GetStream() - { - return new SlicedStream(Assembly.GetManifestResourceStream(AvaloniaResourceName), _offset, _length); - } - } - - class SlicedStream : Stream - { - private readonly Stream _baseStream; - private readonly int _from; - - public SlicedStream(Stream baseStream, int from, int length) - { - Length = length; - _baseStream = baseStream; - _from = from; - _baseStream.Position = from; - } - public override void Flush() - { - } - - public override int Read(byte[] buffer, int offset, int count) - { - return _baseStream.Read(buffer, offset, (int)Math.Min(count, Length - Position)); - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (origin == SeekOrigin.Begin) - Position = offset; - if (origin == SeekOrigin.End) - Position = _from + Length + offset; - if (origin == SeekOrigin.Current) - Position = Position + offset; - return Position; - } - - public override void SetLength(long value) => throw new NotSupportedException(); - - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - - public override bool CanRead => true; - public override bool CanSeek => _baseStream.CanRead; - public override bool CanWrite => false; - public override long Length { get; } - public override long Position - { - get => _baseStream.Position - _from; - set => _baseStream.Position = value + _from; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - _baseStream.Dispose(); - } - - public override void Close() => _baseStream.Close(); - } - - private class AssemblyDescriptor - { - public AssemblyDescriptor(Assembly assembly) - { - Assembly = assembly; - - if (assembly != null) - { - Resources = assembly.GetManifestResourceNames() - .ToDictionary(n => n, n => (IAssetDescriptor)new AssemblyResourceDescriptor(assembly, n)); - Name = assembly.GetName().Name; - using (var resources = assembly.GetManifestResourceStream(AvaloniaResourceName)) - { - if (resources != null) - { - Resources.Remove(AvaloniaResourceName); - - var indexLength = new BinaryReader(resources).ReadInt32(); - var index = AvaloniaResourcesIndexReaderWriter.Read(new SlicedStream(resources, 4, indexLength)); - var baseOffset = indexLength + 4; - AvaloniaResources = index.ToDictionary(r => "/" + r.Path.TrimStart('/'), r => (IAssetDescriptor) - new AvaloniaResourceDescriptor(assembly, baseOffset + r.Offset, r.Size)); - } - } - } - } - - public Assembly Assembly { get; } - public Dictionary Resources { get; } - public Dictionary AvaloniaResources { get; } - public string Name { get; } - } - - public static void RegisterResUriParsers() - { - if (!UriParser.IsKnownScheme("avares")) - UriParser.Register(new GenericUriParser( - GenericUriParserOptions.GenericAuthority | - GenericUriParserOptions.NoUserInfo | - GenericUriParserOptions.NoPort | - GenericUriParserOptions.NoQuery | - GenericUriParserOptions.NoFragment), "avares", -1); - } - } -} diff --git a/src/Web/Avalonia.Blazor/Avalonia.Blazor.csproj b/src/Web/Avalonia.Blazor/Avalonia.Blazor.csproj index dab505087f..d670b4f46d 100644 --- a/src/Web/Avalonia.Blazor/Avalonia.Blazor.csproj +++ b/src/Web/Avalonia.Blazor/Avalonia.Blazor.csproj @@ -1,24 +1,25 @@  - - net6.0 - enable - enable - + + net6.0 + enable + enable + - - - + + + + - - - - + + + + - - - - - + + + + + diff --git a/src/Web/Avalonia.Blazor/BlazorRuntimePlatform.cs b/src/Web/Avalonia.Blazor/BlazorRuntimePlatform.cs index 683ebb3f4b..7267294b7f 100644 --- a/src/Web/Avalonia.Blazor/BlazorRuntimePlatform.cs +++ b/src/Web/Avalonia.Blazor/BlazorRuntimePlatform.cs @@ -2,6 +2,7 @@ using System; using System.Runtime.InteropServices; using System.Threading; using Avalonia.Platform; +using Avalonia.Shared.PlatformSupport; namespace Avalonia.Blazor { @@ -58,4 +59,4 @@ namespace Avalonia.Blazor AvaloniaLocator.CurrentMutable.Bind().ToConstant(new AssetLoader()); } } -} \ No newline at end of file +}